"text":"Forgejo is an open source source control system. As game development not only needs to track text files but large binaries (Images, Videos, Audio etc.), traditional providers like GitHub or GitLab might not scale or become quite pricey. This is why a self-hosted solution was set up.\nThe Forgejo instance is currently hosted on Hetzner Cloud cpx11. For storage, a 1tb Hetzner Storage Box is attached via CIFS. The Forgejo instance can be accessed on https://code.virtuos.world.\n\n\n\nOn a new server, add a new user that is not root:\n\nadduser newusername\nusermod -aG sudo newusername\nsudo -i -u git newusername\n\nCreate git user account and add it to the docker usergroup, then switch to the new user.\n\nsudo adduser --system --shell /bin/bash -gecos 'Git Version Control' --group --disabled-password --home /home/git/ git\nsudo usermod -aG docker git\n\nAutomount the storage box by editing /etc/fstab. Important: Use the UID and GID of the newly created git user! You can get them by typing id git.\n\nsudo nano /etc/fstab\n\nAdd this line\n\n//<username>.your-storagebox.de/backup /mnt/<path> cifs iocharset=utf8,rw,credentials=/etc/backup-credentials.txt,uid=<system account>,gid=<system group>,file_mode=0660,dir_mode=0770 0 0\nCreate the credentials file…\nsudo nano /etc/backup-credentials.txt\n…and fill it with your username and password.\nusername=uXXXXXX\npassword=yourpassword\n\nInstall cifs-utils\n\napt install cifs-utils\n\nMounting might result in an error regarding the iocharset, to fix that do:\n\napt install linux-generic\napt install linux-modules-extra-$(uname -r)\nreboot\n\nMount the storage\n\nsudo mount -a\n\nInstall docker-compose\n\nsudo apt install docker-compose \n\nCreate docker directory\n\nsudo mkdir ~/forgejo\n\nCreate docker-compose file\n\nsudo nano docker-compose.yml\n\nPaste the following content. This assumes your UID is 115 and your GID is 120. Use id -u <username> to find your values. It also maps the repository to use the storage box.\n\n\nEdit: I removed - /mnt/source/git/repositories:/data/git/repositories as it’s slow and hooks won’t work on the Storage Box!\n\nversion: '3'\n\nnetworks:\n forgejo:\n external: false\n\nservices:\n server:\n image: codeberg.org/forgejo/forgejo:1.20\n container_name: forgejo\n environment:\n - USER_UID=115\n - USER_GID=120\n restart: always\n networks:\n - forgejo\n volumes:\n - ./forgejo:/data\n - /etc/timezone:/etc/timezone:ro\n - /etc/localtime:/etc/localtime:ro\n - /mnt/source/git/lfs:/data/lfs\n\n ports:\n - '3000:3000'\n - '222:22'\nFor some reason in my case, the app.ini pointed to /data/git/lfs instead of data/lfs. Fixing this resulted in actually using /mnt/source/git/lfs on the host.\n\nInstall nginx\n\nsudo apt install nginx\n\nSetup firewall\n\nsudo ufw allow OpenSSH\nsudo ufw allow \"Nginx Full\"\nsudoufwenable\n\nOpennginxconfig\n\nsudonano/etc/nginx/sites-available/forgejo\n\nAdd:\n\nserver{\nlisten80;\nlisten[::]:80;\nserver_namemy.domain.tld;\n\nlocation/{\nclient_max_body_size4G;\nproxy_passhttp://localhost:3000;\n proxy_set_header Host $host;\n proxy_set_header X-Real_IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n }\n}\n(This assumes you have registered a domain and added A and AAAA records.)\n\nCreate symlink to actually enable the site\n\nsudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/\n\nInstall Certbot\n\nsudo apt install snapd\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot\n\nGet Let’s Encrypt certificate. For testing, add --test-cert.\n\nsudo certbot --nginx\n\nRun your docker compose\n\ncd ~/forgejo\nsudo docker-compose up -d\n\nTo redirect the homepage to the repository view:\n\nserver {\n server_name code.virtuos.world;\n\n location / {\n client_max_body_size 4G;\n proxy_pass ht
"text":"On a new server, add a new user that is not root:\n\nadduser newusername\nusermod -aG sudo newusername\nsudo -i -u git newusername\n\nCreate git user account and add it to the docker usergroup, then switch to the new user.\n\nsudo adduser --system --shell /bin/bash -gecos 'Git Version Control' --group --disabled-password --home /home/git/ git\nsudo usermod -aG docker git\n\nAutomount the storage box by editing /etc/fstab. Important: Use the UID and GID of the newly created git user! You can get them by typing id git.\n\nsudo nano /etc/fstab\n\nAdd this line\n\n//<username>.your-storagebox.de/backup /mnt/<path> cifs iocharset=utf8,rw,credentials=/etc/backup-credentials.txt,uid=<system account>,gid=<system group>,file_mode=0660,dir_mode=0770 0 0\nCreate the credentials file…\nsudo nano /etc/backup-credentials.txt\n…and fill it with your username and password.\nusername=uXXXXXX\npassword=yourpassword\n\nInstall cifs-utils\n\napt install cifs-utils\n\nMounting might result in an error regarding the iocharset, to fix that do:\n\napt install linux-generic\napt install linux-modules-extra-$(uname -r)\nreboot\n\nMount the storage\n\nsudo mount -a\n\nInstall docker-compose\n\nsudo apt install docker-compose \n\nCreate docker directory\n\nsudo mkdir ~/forgejo\n\nCreate docker-compose file\n\nsudo nano docker-compose.yml\n\nPaste the following content. This assumes your UID is 115 and your GID is 120. Use id -u <username> to find your values. It also maps the repository to use the storage box.\n\n\nEdit: I removed - /mnt/source/git/repositories:/data/git/repositories as it’s slow and hooks won’t work on the Storage Box!\n\nversion: '3'\n\nnetworks:\n forgejo:\n external: false\n\nservices:\n server:\n image: codeberg.org/forgejo/forgejo:1.20\n container_name: forgejo\n environment:\n - USER_UID=115\n - USER_GID=120\n restart: always\n networks:\n - forgejo\n volumes:\n - ./forgejo:/data\n - /etc/timezone:/etc/timezone:ro\n - /etc/localtime:/etc/localtime:ro\n - /mnt/source/git/lfs:/data/lfs\n\n ports:\n - '3000:3000'\n - '222:22'\nFor some reason in my case, the app.ini pointed to /data/git/lfs instead of data/lfs. Fixing this resulted in actually using /mnt/source/git/lfs on the host.\n\nInstall nginx\n\nsudo apt install nginx\n\nSetup firewall\n\nsudo ufw allow OpenSSH\nsudo ufw allow \"Nginx Full\"\nsudoufwenable\n\nOpennginxconfig\n\nsudonano/etc/nginx/sites-available/forgejo\n\nAdd:\n\nserver{\nlisten80;\nlisten[::]:80;\nserver_namemy.domain.tld;\n\nlocation/{\nclient_max_body_size4G;\nproxy_passhttp://localhost:3000;\n proxy_set_header Host $host;\n proxy_set_header X-Real_IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n }\n}\n(This assumes you have registered a domain and added A and AAAA records.)\n\nCreate symlink to actually enable the site\n\nsudo ln -s /etc/nginx/sites-available/forgejo /etc/nginx/sites-enabled/\n\nInstall Certbot\n\nsudo apt install snapd\nsudo snap install --classic certbot\nsudo ln -s /snap/bin/certbot /usr/bin/certbot\n\nGet Let’s Encrypt certificate. For testing, add --test-cert.\n\nsudo certbot --nginx\n\nRun your docker compose\n\ncd ~/forgejo\nsudo docker-compose up -d\n\nTo redirect the homepage to the repository view:\n\nserver {\n server_name code.virtuos.world;\n\n location / {\n client_max_body_size 4G;\n proxy_pass http://localhost:3000;\n proxy_set_header Host $host;\n proxy_set_header X-Real_IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n }\n\n location = / {\n return 301 https://$host/explore/repos;\n }\n\n listen [::]:443 ssl ipv6only=on; # managed by Certbot\n listen 443 ssl; # managed by Certbot\n ssl_certificate /etc/letsencrypt/live/code.virtuos.world/full
"text":"On the host, the path starts from the folder with your docker-compose file, /forgejo/gitea/public/assets/\nPaste the images with the respective names listed below:\n\npublic/img/logo.svg - Used for site icon, app icon\n\npublic/img/logo.png - Used for Open Graph\n\npublic/img/avatar_default.png - Used as the default avatar image\n\npublic/img/apple-touch-icon.png - Used on iOS devices for bookmarks\n\npublic/img/favicon.svg - Used for favicon\n\npublic/img/favicon.png - Used as fallback for browsers that don’t support SVG favicons",
"text":"Installation on Uberspace\n\nDownload latest release package, upload and untar:\n\ntar -xf <name.tar.gz>\n\nCreate an empty MySQL database:\n\nmysql -e \"CREATE DATABASE <username>_leantime\"\n\nPoint your domain to the public/ directory\nEdit /leantime/public/.htaccess:\n\nOptions +SymLinksIfOwnerMatch\nRewriteEngine On\nRewriteBase /\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteRule ^ index.php [L]\n\nGet your MySQL credentials:\n\nmy_print_defaults client\n\nFill in your database credentials (username, password, host, dbname) in config/sample.env\n\nRename config/sample.env to config/.env\n\nNavigate to <yourdomain.com>/install\n\nFollow instructions to install database and set up first user account",
"text":"This is the documentation for VIRTUOS WORLD, the Open Source VR Game I develop during my PhD in chemistry didactics. The main purpose of this page is to share the insights I gained during development.\nIf you want to take a look inside a project, there are currently four important repositories:\n\nVIRTUOS_Playground: The repository contains the Unreal Engine Project\nVIRTUOS_Assets: Contains all Project related assets like 3D-Models, Graphics etc.\nVIRTUOS_Documents: Contains all written Documents\nVIRTUOS_Docs: The Documentation of this Project (which you’re reading right now)",
"text":"If you want to take a look inside a project, there are currently three important repositories:\n\nVIRTUOS_Playground: The repository contains the Unreal Engine Project\nVIRTUOS_Assets: Contains all Project related assets like 3D-Models, Graphics etc.\nVIRTUOS_Documents: Contains all written Documents\nVIRTUOS_Docs: The Documentation of this Project (which you’re reading right now)\n\n\nSetting up the project with UE5\nIf you’re familiar with Git, you’ll likely already know how to get the files from the repository:\ngit pull https://code.virtuos.world/VIRTUOS_WORLD/VIRTUOS_Docs.git\nIf you’re not comforatable with the command line, you can install a Git GUI like Github Desktop:\nHit File -> Clone repository\n\nEnter the repository from code.virtuos.world you want to clone, e.g.https://code.virtuos.world/VIRTUOS_WORLD/VIRTUOS_Playground.git\n\n\n\nDependencies\nThe project uses the following external Plugins:\n\nVR Expansion Plugin\nSPUD: Steve’s Persistent Unreal Data library\nSUDS: Steve’s Unreal Dialogue System\nStevesUEHelpers: Steve’s UE Helper Plugin Library\n\nThe Plugins are already in the /Plugins folder and should build with the project without errors. For manual installation, see the respective Documentation.\n\n\nBuilding the project\nTo build the project, you’ll need Visual Studio installed on your system.\n\n\nRight click the .uproject file and hit Generate Visual Studio project files.\n\n\nOpen the newly generated .sln file in Visual Studio. From there, hit Build -> Build solution.\n\n\nNote: This may take a while.",
"text":"Important Terminology\n\n\nLevels/Maps\nThe word ‘map’ generally refers to what the average person calls a ‘level’ and may be used interchangeably. See this term’s history here.\n\n\nIdentifiers\nAn Identifier is anything that resembles or serves as a “name”. For example, the name of an asset, or the name of a material later, or a blueprint property, a variable, or a folder name, or for a data table row name, etc…\n\n\n\nCases\nThere are a few different ways you can CaseWordsWhenNaming. Here are some common casing types:\n\nPascalCase\nCapitalize every word and remove all spaces, e.g.DesertEagle, StyleGuide, ASeriesOfWords.\ncamelCase\nThe first letter is always lowercase but every following word starts with uppercase, e.g.desertEagle, styleGuide, aSeriesOfWords.\nSnake_case\nWords can arbitrarily start upper or lowercase but words are separated by an underscore, e.g.desert_Eagle, Style_Guide, a_Series_of_Words.\n\n\n\n\nVariables / Properties\nThe words ‘variable’ and ‘property’ in most contexts are interchangable. If they are both used together in the same context however:\n\n\nProperty\nUsually refers to a variable defined in a class. For example, if BP_Barrel had a variable bExploded, bExploded may be referred to as a property of BP_Barrel.\nWhen in the context of a class, it is often used to imply accessing previously defined data.\n\n\n\nVariable\nUsually refers to a variable defined as a function argument or a local variable inside a function.\nWhen in the context of a class, it is often used to convey discussion about its definition and what it will hold.",
"text":"0. Principles\nThese principles have been adapted from idomatic.js style guide.\n\n\n0.1 If your UE4 project already has a style guide, you should follow it.\nIf you are working on a project or with a team that has a pre-existing style guide, it should be respected. Any inconsistency between an existing style guide and this guide should defer to the existing.\nStyle guides should be living documents. You should propose style guide changes to an existing style guide as well as this guide if you feel the change benefits all usages.\n\n“Arguments over style are pointless. There should be a style guide, and you should follow it.”\nRebecca Murphey\n\n\n\n\n0.2 All structure, assets, and code in any Unreal Engine 4 project should look like a single person created it, no matter how many people contributed.\nMoving from one project to another should not cause a re-learning of style and structure. Conforming to a style guide removes unneeded guesswork and ambiguities.\nIt also allows for more productive creation and maintenance as one does not need to think about style. Simply follow the instructions. This style guide is written with best practices in mind, meaning that by following this style guide you will also minimize hard to track issues.\n\n\n\n0.3 Friends do not let friends have bad style.\nIf you see someone working either against a style guide or no style guide, try to correct them.\nWhen working within a team or discussing within a community such as Unreal Slackers, it is far easier to help and to ask for help when people are consistent. Nobody likes to help untangle someone’s Blueprint spaghetti or deal with assets that have names they can’t understand.\nIf you are helping someone whose work conforms to a different but consistent and sane style guide, you should be able to adapt to it. If they do not conform to any style guide, please direct them here.\n\n\n\n0.4 A team without a style guide is no team of mine.\nWhen joining an Unreal Engine 4 team, one of your first questions should be “Do you have a style guide?”. If the answer is no, you should be skeptical about their ability to work as a team.\n\n\n\n0.5 Don’t Break The Law\nGamemakin LLC is not a lawyer, but please don’t introduce illegal actions and behavior to a project, including but not limited to:\n\nDon’t distribute content you don’t have the rights to distribute\nDon’t infringe on someone else’s copyrighted or trademark material\nDon’t steal content\nFollow licensing restrictions on content, e.g.attribute when attributions are needed",
"text":"00. Globally Enforced Opinions\n@TODO: Make this section 1 and update this document accordingly. Or maybe we don’t?\n\n\n00.1 Forbidden Characters\n\nIdentifiers\nIn any Identifier of any kind, never use the following unless absolutely forced to:\n\nWhite space of any kind\nBackward slashes \\\nSymbols i.e.#!@$%\nAny Unicode character\n\nAny Identifier should strive to only have the following characters when possible (the [A-Za-z0-9_]+)\n\nABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n1234567890\n_ (sparingly)\n\nThe reasoning for this is this will ensure the greatest compatibility of all data across all platforms across all tools, and help prevent downtime due to potentially bad character handling for identifiers in code you don’t control.",
"text":"4. Static Meshes\nThis section will focus on Static Mesh assets and their internals.\n\nSections\n\n4.1 UVs\n\n\n4.2 LODs\n\n\n4.3 Modular Socketless Snapping\n\n\n4.4 Must Have Collision\n\n\n4.5 Correct Scale\n\n\n\n\n\n4.1 Static Mesh UVs\nIf Linter is reporting bad UVs and you can’t seem to track it down, open the resulting .log file in your project’s Saved/Logs folder for exact details as to why it’s failing. I am hoping to include these messages in the Lint report in the future.\n\n\n\n4.1.1 All Meshes Must Have UVs\nPretty simple. All meshes, regardless how they are to be used, should not be missing UVs.\n\n\n\n\n4.1.2 All Meshes Must Not Have Overlapping UVs for Lightmaps\nPretty simple. All meshes, regardless how they are to be used, should have valid non-overlapping UVs.\n\n\n\n\n\n4.2 LODs Should Be Set Up Correctly\nThis is a subjective check on a per-project basis, but as a general rule any mesh that can be seen at varying distances should have proper LODs.\n\n\n\n\n4.3 Modular Socketless Assets Should Snap To The Grid Cleanly\nThis is a subjective check on a per-asset basis, however any modular socketless assets should snap together cleanly based on the project’s grid settings.\nIt is up to the project whether to snap based on a power of 2 grid or on a base 10 grid. However if you are authoring modular socketless assets for the marketplace, Epic’s requirement is that they snap cleanly when the grid is set to 10 units or bigger.\n\n\n\n\n4.4 All Meshes Must Have Collision\nRegardless of whether an asset is going to be used for collision in a level, all meshes should have proper collision defined. This helps the engine with things such as bounds calculations, occlusion, and lighting. Collision should also be well-formed to the asset.\n\n\n\n\n4.5 All Meshes Should Be Scaled Correctly\nThis is a subjective check on a per-project basis, however all assets should be scaled correctly to their project. Level designers or blueprint authors should not have to tweak the scale of meshes to get them to confirm in the editor. Scaling meshes in the engine should be treated as a scale override, not a scale correction.\n⬆ Back to Top",
"crumbs":[
"Homepage",
"Styleguide"
]
},
{
"objectID":"sites/styleguide.html#niagara",
"href":"sites/styleguide.html#niagara",
"title":"Styleguide",
"section":"5. Niagara",
"text":"5. Niagara\nThis section will focus on Niagara assets and their internals.\n\nSections\n\n5.1 Naming Rules\n\n\n\n\n\n5.1 No Spaces, Ever\nAs mentioned in 00.1 Forbidden Identifiers, spaces and all white space characters are forbidden in identifiers. This is especially true for Niagara systems as it makes working with things significantly harder if not impossible when working with HLSL or other means of scripting within Niagara and trying to reference an identifier.\n(Original Contribution by @dunenkoff)\n⬆ Back to Top",
"crumbs":[
"Homepage",
"Styleguide"
]
},
{
"objectID":"sites/styleguide.html#levels-maps",
"href":"sites/styleguide.html#levels-maps",
"title":"Styleguide",
"section":"6. Levels / Maps",
"text":"6. Levels / Maps\nSee Terminology Note regarding “levels” vs “maps”.\nThis section will focus on Level assets and their internals.\n\nSections\n\n6.1 No Errors Or Warnings\n\n\n6.2 Lighting Should Be Built\n\n\n6.3 No Player Visible Z Fighting\n\n\n6.4 Marketplace Specific Rules\n\n\n\n\n\n6.1 No Errors Or Warnings\nAll levels should load with zero errors or warnings. If a level loads with any errors or warnings, they should be fixed immediately to prevent cascading issues.\nYou can run a map check on an open level in the editor by using the console command “map check”.\nPlease note: Linter is even more strict on this than the editor is currently, and will catch load errors that the editor will resolve on its own.\n\n\n\n\n6.2 Lighting Should Be Built\nIt is normal during development for levels to occasionally not have lighting built. When doing a test/internal/shipping build or any build that is to be distributed however, lighting should always be built.\n\n\n\n\n6.3 No Player Visible Z Fighting\nLevels should not have any z-fighting in all areas visible to the player.\n\n\n\n\n6.4 Marketplace Specific Rules\nIf a project is to be sold on the UE4 Marketplace, it must follow these rules.\n\n\n\n\n6.4.1 Overview Level\nIf your project contains assets that should be visualized or demoed, you must have a map within your project that contains the name “Overview”.\nThis overview map, if it is visualizing assets, should be set up according to Epic’s guidelines.\nFor example, InteractionComponent_Overview.\n\n\n\n\n6.4.2 Demo Level\nIf your project contains assets that should be demoed or come with some sort of tutorial, you must have a map within your project that contains the name “Demo”. This level should also contain documentation within it in some form that illustrates how to use your project. See Epic’s Content Examples project for good examples on how to do this.\nIf your project is a gameplay mechanic or other form of system as opposed to an art pack, this can be the same as your “Overview” map.\nFor example, InteractionComponent_Overview_Demo, ExplosionKit_Demo.\n⬆ Back to Top",
"crumbs":[
"Homepage",
"Styleguide"
]
},
{
"objectID":"sites/styleguide.html#textures-1",
"href":"sites/styleguide.html#textures-1",
"title":"Styleguide",
"section":"7. Textures",
"text":"7. Textures\nThis section will focus on Texture assets and their internals.\n\nSections\n\n7.1 Dimensions Are Powers of 2\n\n\n7.2 Texture Density Should Be Uniform\n\n\n7.3 Textures Should Be No Bigger than 8192\n\n\n7.4 Correct Texture Groups\n\n\n\n\n\n7.1 Dimensions Are Powers of 2\nAll textures, except for UI textures, must have its dimensions in multiples of powers of 2. Textures do not have to be square.\nFor example, 128x512, 1024x1024, 2048x1024, 1024x2048, 1x512.\n\n\n\n\n7.2 Texture Density Should Be Uniform\nAll textures should be of a size appropriate for their standard use case. Appropriate texture density varies from project to project, but all textures within that project should have a consistent density.\nFor example, if a project’s texture density is 8 pixel per 1 unit, a texture that is meant to be applied to a 100x100 unit cube should be 1024x1024, as that is the closest power of 2 that matches the project’s texture density.\n\n\n\n\n7.3 Textures Should Be No Bigger than 8192\nNo texture should have a dimension that exceeds 8192 in size, unless you have a very explicit reason to do so. Often, using a texture this big is simply just a waste of resources.\n\n\n\n\n7.4 Textures Should Be Grouped Correctly\nEvery texture has a Texture Group property used for LODing, and this should be set correctly based on its use. For example, all UI textures should belong in the UI texture group.\n⬆ Back to Top",
"text":"In Class: BP_TraceController | Based on VREs BP_TeleportTrace\nTo make grabbing more comfortable and accessible, I implemented a pull-grab mechanic inspired by Half Life Alyx. While the VR Expansion Plugin already implements logic for tossing objects towards the player, the template and many other resources rely on a line trace to get the object to toss. This results in multiple problems, above all the player having to aim very precisely and objects blocking each other.\nTo target these issues, I use a multi sphere trace to get all actors in range, saving them to an array. For each, we can check their angle to the hand via the dot product between the hand->forward and hand->object vectors and save them to an array as well. By getting the min of the of the dot product array, we can retrieve the correct object via the index (could’ve also used a map).\nFunction: Trace For Tossable Objects\n\nFunction: Get Angle To Hand\n\nThe trace relies on its own collision channel named TossableTrace, allowing for objects like cabinet doors to block this specific trace. As this blocks access to compartments smaller than the trace, there is a second smaller trace doing the same job (only if larger trace is blocked, might be further optimizable).\nAs it might not favorable for all components on the actor to be tossable, the Item Data Component (originally introduced for the Inventory) holds an array of primitive components which are quickly added via a library macro on the respective actor (you can’t choose primitive components via the dropdown on the component).\nThe ribbon between the object and the hand is drawn via a Niagara System getting passed an array of locations. While I was aware of this technique from how the default UE5 VR Template renders its teleport-preview, there is also a writeup by Steve Streeting if you need further information.\nNote: The toss from VRE needs “Simulation Generates Hit Events” enabled to work correctly.",
"crumbs":[
"Homepage",
"Code Snippets",
"Alyx Grab"
]
},
{
"objectID":"sites/codesnippets/Slicegame.html",
"href":"sites/codesnippets/Slicegame.html",
"title":"Slicegame",
"section":"",
"text":"The Slicegame is my attempt at implementing a Beatsaber-Style Rhythm-Game. It explores Unreals Harmonix Plugin for parsing Midi in Metasounds and Geometry Script for real time boolean operations.\nGiven a Midi File, the system in BP_DynamicObjectSpawner spawns instances from the class BP_DynamicHitObject (the thing you want to hit) on a 3x3 Grid, moves them towards a location over time so it’s always at to correct location (PlayerPosition + ArmLength) on the beat. The Midi also encodes from which direction the HitObject should be hit and saved in an Enum in the class during spawn.\n\n\n\nMidi Setup in Reaper\n\n\nCorrect Hit detection to me several approaches, its a bit tricky because while our objects (or rather their hit indictors) don’t move relative to the character, their angle to the player does change when the objects move from front to back while the player forwards or sideways. The hit detection therefore needs to take the characters view vector into account. From there, you can create up and right vectors and compare them direction of the sword via the dot product.\nIn Class : BP_DynamicHitObject | Function: GetHitType\n\nAs both velocity (sword is wiggly) and impact normal (surface doesn’t always point in hit direction) vectors you can get from the hit event turned out to be rather unreliable for detection sword direction, I went with sampling the impact point on hit, attaching that point to the sword by converting the location into the relative space of the sword. After a short delay, that point, virtually attached to sword, has moved. By converting that point back to world transform, we can calculate the direction by subtracting hit location and delay sampled location. This works quite reliably.\nIn Class : BP_DynamicHitObject",
"crumbs":[
"Homepage",
"Code Snippets",
"Slicegame"
]
},
{
"objectID":"sites/codesnippets/GripRobot.html",
"href":"sites/codesnippets/GripRobot.html",
"title":"Clarice / Grippable Robot",
"section":"",
"text":"What. A. Pain. In Class: BP_Clarice\nFor the main NPC that guides the player - Clarice - I wanted a high degree of interactability. This involves grabbing the robot, e.g.to throw them somewhere to solve riddles, use them as a key or other shenanigans. I also wanted them to be rather flexible when it comes to locomotion (Walk / Fly / Jump / Roll) for it to able to follow the player anywhere.\nHere are some learning from implementing: - Your characters physics asset NEEDS a root Body. I personally still struggle with Physics Assets. -\n\n\n\n\n\n\nThank you\n\n\n\nA big thank you to Joshua “MordenTral” Statzer (Developer of the VREP) who took personal time to look into my issues with grabbing Skeletal Meshes. The first and only time someone helped me out personally in this project, and also doing so in their free time.",
"crumbs":[
"Homepage",
"Code Snippets",
"Clarice / Grippable Robot"
]
},
{
"objectID":"sites/codesnippets/CaptchaDoor.html",
"href":"sites/codesnippets/CaptchaDoor.html",
"title":"Captcha Door",
"section":"",
"text":"Example for BlueprintUE",
"crumbs":[
"Homepage",
"Code Snippets",
"Captcha Door"
]
},
{
"objectID":"sites/codesnippets/Inventory.html",
"href":"sites/codesnippets/Inventory.html",
"title":"Inventory",
"section":"",
"text":"The Inventory uses a scalable, circular array of widget components the user can put objects in and draw objects out. When the inventory is opened, the player moves their hand towards a widget, which then gets activated. Depending of the content of the widget and content of the hand, an action (put in, pull out, replace) is triggered on inventory close. Compared to approaches based on a line trace/widget interactor, I use the distance to the hand for a quicker and “sloppier” interaction.\nItem Data is handled via an Item Data Component, following the guidance of Ryan Laleys Inventory System Tutorial Series. The Item Data is drawn from a Data Table.",
"text":"Function: GetAngleToHand\n\nAs it might not favourable for all components of an actor to be tossable, the Item Data Component (originally introduced for the Inventory) holds an array of primitive components which are quickly added via a library macro on the respective actor (you can’t choose primitive components via the dropdown on the component).\nThe trace relies on its own collision channel, allowing for objects like cabinet doors to block this specific trace. As this might block access to compartments smaller than the trace, there is a second smaller trace doing the same job (might be optimizeable)."
"text":"In Class: MovablesSoundComponent\nThis Component is an Actor Component to designed to be attached to Actors that generate Sounds based on their physical movement. This includes standalone objects like a ball or components like drawers in cabinet.\nDetecting Hit and Slide Movements\nWhen one object hit another, we expect it to make some kind of hit sound, depending on their qualities (size, material, surface texture etc.) and the hit velocity. A special case is dragging an object across a surface. Although this causes repetitive hits, we don’t expect the Hit Sound to be played repetitively but a dedicated “slide sound”. To check wether a Hit Event from the simulation should trigger a Hit Sound or a Slide Sound, we can use the velocity vector of the hit component and compare it to to the Hit Normal Vector. When we slide across a surface, the velocity is perpendicular to the impact normal. Using acosd, you can convert the dot product to an angle, which can be easier to work with. Based on an Angle Threshold, you can decide whether a slide should be considered a slide.\n\n\n\n\n\n\nA thought\n\n\n\nNow that I’m writing it down, you could also make the Threshold depending on the velocity, resulting in slow movements to be more susceptible to “clonks”",
"crumbs":[
"Homepage",
"Code Snippets",
"Movables Sound Component"
]
},
{
"objectID":"sites/codesnippets/MainMenu.html",
"href":"sites/codesnippets/MainMenu.html",
"title":"Main Menu",
"section":"",
"text":"In Class: BP_MainMenu\nThe main menu is composed of several widget components in 3D-Space. The Widget Index defines the depth of the menu, e.g.the widget showing the Audio Settings Widget is accessed from Settings (from Main at Index 0) -> Audio (from Settings at Index -1) -> Audio Widget (at Index -2). Opening a new Menu causes all Widget Indices to increment. Widget with an Index lower than 0 gets collapsed, Widgets with an Index greater 0 get disabled and only at Index 0, the Widget is enabled. On disabled widgets, the last pressed button is altered visually which also gets reset at Index 0.\nAll Widget Buttons inherit from the same widget class implementing an enum to save the button function. The button actions are centralized by binding to a custom event dispatcher on the click event, passing the enum to the BP_MainMenu. From there, we can switch on enum and handle the respective functionality. The main thing we need to do manually is running the UpdateIndices Function to increment and making the correct Widget visible (we can’t do this automatically as there are multiple Widgets with the same Index).",
"text":"In Class: CustomAIController\nWhile Navigation builds on the UE AI Controller / Character Movement with NavMeshes, I wanted to implement a more precise way to control the path a NPC takes. Turns out, Unreal doesn’t seem to have a “Follow Spline” logic inbuilt, which I found to be a bit trickier than expected. It’s currently based of this Tutorial, which calculates a speed factor from Max Walk Speed and Spline Length and then continuously moves the Actor to the new Point along the Spline at that Value. Adding Jump functionality with this approach added extra complexity as the timeline needs to stop at the correct distance from the Character for the AI not to get confused.",
"text":"To handle SaveGames and Persistence in Level Streaming, SPUD is used. The WB_CustomSaveLoadScreen used in the Main Menu is largely based of the SPUD Examples SaveLoadScreen Widget.\nMost Variables are passed to the GameInstance, which is especially important for variables that need to exist before the game is loaded, e.g.on the initial Main Menu Map.\n\n\n\n\n\n\nNote\n\n\n\nSaving the GameInstance with SPUD via BP should’ve been be very straight forward, but I was stuck there for multiple days. Eventually I opened up a Github-Issue and it was confirmed that the SPUD Subsystem is not ready on Event Init of the BP GameInstance. I went on implementing going the C++ way, but a delay until the Subsystem is ready should fix that.\n\n\nPulling Actors into the persistent Levels\nAs a lot objects in VR are interactible and can therefore easily be moved by the player, it’s possible that movable objects leave their streaming level boundary and get despawned when their owning level is unloaded. We have to possibilities to fix that: Having all those objects on the persistent level by default, which isn’t ideal from an optimization perspective, or move them to the persistent level when the player actually interacts with them. From my knowledge, there is no proper way of changing the owning level at runtime but destroying and respawning the actor, which isn’t always ideal.",