Difference between revisions of "Restless Souls/Wishlist"
m (game icon, intro splash, intro video section moved)
(mouse cursor notes moved)
|Line 479:||Line 479:|
Latest revision as of 20:48, 15 August 2019
The main page for UnrealOni can be found HERE.
- 1 Oni 2 game
- 2 Oni 2 game tech demo with Unreal Engine 4
- 2.1 Goal
- 2.2 Stuff that is probably coverd by the tech demo
- 2.3 Practice notes
- 2.4 Tutorial summary: 3rd Person Game
- 2.5 Selected knowledge
- 2.5.1 Code management
- 2.5.2 Game instance
- 2.5.3 Main func
- 2.5.4 Cameras
- 2.5.5 Music
- 2.5.6 Meshes
- 2.5.7 Sockets
- 2.5.8 Powerups
- 2.5.9 HUD
- 2.5.10 Widgets
- 2.5.11 Animations
- 2.5.12 Normal maps
- 2.5.13 BSL equivalents
- 2.5.14 Cooking the game
- 2.5.15 Exit the game
- 2.6 Videos
Oni 2 game
If we had a real option to create an Oni 2 with modern engine I would root for Unreal Engine 4.
It works on PC and Macs and it is free for non-commercial projects.
There's a big community, documentation and tutorials.
If the demo is going to be extended it would be better to work with original/free content from begin with, sparing us IP concerns. Old content is meant as placeholders.
It supports weapon and h2h combat. Of course Oni's unique fighting style would have be re-written. But everything else should be supported by the engine. Particle, trigger volumes, wide environments, details characters. What else could you wish for?
Besides new levels, characters and story... How about new gameplay elements? Since the Daodan is an core element of the story it would be shame not to make use of it. It would add a strategical component and adds to the replay value.
Since the bio aspects would be hard to reinvent for a spiritual successor, one might more concentrate on good 'ol nanotech. "Omega days"? "Iron demons?"
Primary Daodan traits
- Healing (stopping a heavy bleeding and recovery speed)
- Power (stronger attacks, throwing or using heavy objects and enemies as weapons (e.g. using a skriker as Nunchaku with maxed out power))
- Speed (faster attacks, running, reflexes (dodging, counters))
- Adapting (mutation speed (biological computing by means of "Oni cluster" cells))
- Mechanical resistance (adaptions to pressure and kinetic damage (e.g. graphene enhanced cells))
- Physical resistance (VDG, plasma, phase stream projectors (electric isolation))
- Chemical resistance (acid, poison (lead bullets, mercury bow bullets))
- Biological resistance (screamer cells, infections by untreated wounds)
- Thermal resistance (extreme heat - fire, extreme coldness)
- Nuclear resistance (depleted uranium ammunition and surviving contaminated areas)
Basically you can "tank up" against weapons and don't need to worry about ammo.
To keep balance you could add more dangerous weapons as the game progresses.
Secondary Daodan traits
The Daodan is not almighty. It has certain weaknesses you should watch out for.
- Your Daodan abilities depend on Daodan biomass which means you can either unlock new abilities or enhance existing ones. And there's a limit to your abilities, because your cell count is limited.
- There should be one or two "ultimates" in Imago stage that depend on your evolution.
- Your number of Daodan cells depend on taken damage and recovery that happen in the last level.
- But too much damage can cause too high growth rates (temporary sudden pain and fever resulting in moments of reduced vision, shooting accuracy, "getting blocked" rate) or it simply kills you because you exceeded the maximal healing capacity of the daodan.
- Sytropin can help you in lowering growth rates. Be aware of increasing drug resistance as the game progresses.
- Reverting an existing mutation takes more "work" than establishing a new one. Basically your course of action is directed evolution.
Improved movement set and h2h
- prone mode
- sneaking at walls and through vent channels
- throw foes over obstacles
- extend h2h with Parkour elements (e.g. wall jump connectable to a flying knee)
- use pickable objects as weapons (either throwable or usable as a club)
- ledge grab (not a must-have)
- swimming (not a must-have)
- controllable drones, robots, mechs, vehicles
- dynamic lighting, wearable lights (flash lights) for dark environments
- temperature affects characters (not a must-have)
- emotions affects characters (not a must-have, rage enhances Daodan powers to a limited degree)
Oni 2 game tech demo with Unreal Engine 4
There is big skepticism inside the community whether an Oni-like game can be accomplished with the Unreal 4 engine.
Therefore I seek to create a technical demo to proof feasibility of such project.
The availability of XML documentation for Oni 1 seem to have speeded up mod creation so I will pursue a similar route for this project. The documentation written here in the process shall serve as measure to lower the learning curve of other modders (or then programmers / content creators).
Stuff that is probably coverd by the tech demo
Everyone could pickup one of these points and investigate how to implement it.
If successful that person would write down a mini-tutorial here on OniGalore so others could see progress and learn from the documentation to perhaps pickup the next, somewhat more complex point.
There could be project files on a cloud drive as long as there aren't too many people involved (otherwise a SVN). New files could be uploaded into a revision folder. Users could test the changes and then update the project in the cloud.
- V - video
- T - tutorial
- D - documentation
Features the demo should include:
- two levels (to test normal level load routine and level streaming (T))
- skydome (V)
- main menu
- So far I failed to add background music. This gave rise to the idea of having an own level for the main menu where you can spawn ambient music. Muro or another character could be previewed with their current movement set and equipment. Shooting the camera would make the game quit. This will be useful when the enemy takes over this HQ AI and he needs to shut it down. "Mission failed." "Screw you!" Pressing the "V" key would allow you to go into your training room or HQ testing new moves or doing investments.
- HUD ("circular" health bar, item slots)
- background music (D) (T) (V)
- save game state
- read game state
- text pages for diary, etc.
- fbx import of old objects
- animated objects with collision box to stand on
- objects that can be thrown
- CJBO equivalents (characters, console, door, trigger (laser), trigger volume, ...)
- compass (objective)
- FBX import and usage of old characters
- combining character parts to one model
- rigging the model
- minimum TRAC-TRAM integration I (walk cycle and idle)
- prone mode
- keyboard and mouse controls
- functional animations
- character h2h (force-sensitive) (a.k.a physical animation component)
- character h2h
- hit reaction
- minimum TRAC-TRAM integration II (combat idle, punch, kick, block, half-damage, unblockable, super)
- minimum TRAC-TRAM integration III (throws)
- minimum TRAC-TRAM integration IV (throwable objects)
- overlay animations
- h2h weapons
- ledge climbing
- cover (AI)
- healing by hypo and level areas
- particle (attachable to env, weap, char)
- weather (rain, snow, wind, dust, lightning)
- two weapons
- ammo type: ballistic ammo
- ammo type: energy cell
- patrol paths and pathfinding
- recreation of basic ONCC properties (incl. shooting inaccuracy)
- spawn AI
- h2h-specific animations
- got hit animation
- got hit animation (CBPM-specific)
- throws mechanics
- follow me command, stop command
- alarm behavior
- CBPM-specific bullet hit behavior
- add effects via ONIE-TMBD-MTRL mini complex
- driving a truck
- BSL equivalent ("Blueprint")
- team support (easy methods to make project contributions)
- levels of difficulty
- Easy: -20%? health for foes
- Hard: +20%? health for foes, foe class upgrade?
- Extreme: shorter reaction times of AI ("killmequick")
- powerups on the ground can be destroyed quite easily by projectiles or heavy h2h attacks
- ammo -> blast damage and fire
- cell -> blast damage and stun effect
- hypo -> little blast damage and a cloud of healing gas
- shield -> major blast damage
- invis -> blinding
- LSI -> mission failed ?
- powerups on the ground can be destroyed quite easily by projectiles or heavy h2h attacks
- YOLO: Extreme mode plus when player dies he cannot reload that savegame. He has to restart at level 1.
- YOLO +: YOLO plus the games has never been played before. first play after installation. This is a secrete achievement.
Besides from what all should be tested and made, here's a groundwork tutorial: Blueprint 3rd Person Game
It touches pretty much stuff in those 4 hours: getting a character into a level, setting him up as player, the controls, animations cycles, animation blending, animation overlay and attaching physics
You could probably split this thing into three parts: learning the basics, adding stuff from the long feature list (those not already covered by the basics), and tweaking animations controls to behave like Oni 1
Now then if you want to try this do the following.
- For content creation you maybe want to use Blender since it is free and available for PC, Mac and Linux.
- Install Visual Studio 2017 Community (to write C++ later)
- Register at https://www.unrealengine.com/en-US/what-is-unreal-engine-4, while logged in you can download the installer from the upper right corner. (Blue button "DOWNLOAD".)
- Start Installer and download Unreal Engine 4.17.1 (It will be about 6 GB. It extracts to 17 GB.)
- Learn viewport navigation from the yellow flashing blue hat icon.
- Then continue with the videos of the 3rd Person Game tutorials.
Tutorial summary: 3rd Person Game
It seems like there aren't scripts anymore. Blueprint is Visual scripting, you are working with visualized functions, objects, etc. It's more what you can do with the C++ code which also gets compiled.
FBX can contain data for TRBS, TRIA, TRAC, multiple TRAM, materials, textures.
- Video ?
- Blend Parameter now on the left side.
Keybinding (equivalent to key_config.txt)
Input to move binding (equivalent to StNA)
Support for Keyboard, Mouse (and inverted control), Gamepads and touch
Player camera settings editable (distance holder named "Spring Arm")
- Animation cycles (TRAM - shortcuts)
- Transition rules (similar to those in TRAM headers <FromState>, <ToState>)
- Blend Space is an heavily enhanced version of <Interpolation>
- The animation gets adapted in length AND rotation values. When properly used, transition are nothing less than perfect.
- Video 8
- Anim Preview Editor is on the right now
- Event Graph: right-click for context menu to add missing "Try Get Pawn Owner"
- Video 12
- Character Blueprint is sort of an ONCC. They aren't exactly equal.
- It misses seeing, hearing, weapon skills and more. It has settings for jump, "air control", LOD, shadow, few CHAR events, collision box, tags, ONIE related stuff
EventGraph -> Component -> Mesh -> e.g. Get Material Index
Whenever you edited a Blueprint: compile (F7) and save (Ctrl + S).
In order to sort things and avoid code duplication I'm trying to spit things for now the following way:
- game related code such as a level loading function and menu references goes to the Game Instance
- level related code such as a Slash Screen function goes to the Level blueprint
- player related code such as keyboard inputs go to a custom Player Controller (changeable in the Game Mode)
- AI related code goes to the ONCC component blueprint
When we store information in an game instance it won't disappear after loading the next level.
Let's say there is a menu button or hotkey in a pause menu widget to disable hint texts.
Using variables in a widget is a dead end. They are destroyed as soon as we close the widget.
We could save our choice of showing the hint text in the level blueprint but as soon as we proceed to the next level the hint text would be shown again.
So, we go up in the hierarchy and store the information in the game instance.
To make the information really persistent (between game starts or when loading a save game) all we would need to do is saving the information from the game instance to a save game.
Equivalent to Oni's main func can be found / created in the "Level Blueprint".
Blueprints > Open Level Bluprints
Right-click to open context menu. Search for and add node "Event BeginPlay"
At this point it's empty.
Maybe for testing or learning purposes add another node: "Play Sound 2D".
Change its Sound source to a Sound Cue you made.
Connect the nodes Event BeginPlay with Play Sound 2D.
You can copy-paste blueprints and share them with others in plain text.
Example of an exit function. (In the actual game use Escape key to exit.)
Begin Object Class=/Script/BlueprintGraph.K2Node_InputKey Name="K2Node_InputKey_0" InputKey=Escape NodePosX=864 NodePosY=-832 NodeGuid=99076AC847F4C36E70B54AA78927F135 CustomProperties Pin (PinId=92DEBC9A43D657DB995617849A0B7927,PinName="Pressed",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,LinkedTo=(K2Node_CallFunction_44 BC9E715842362EDD413EF8A39B14F625,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=3CDB2A9D40427A0CCFDABF8F090CE41F,PinName="Released",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=02AB461F46584F140257A5BC03E90865,PinName="Key",Direction="EGPD_Output",PinType.PinCategory="struct",PinType.PinSubCategory="",PinType.PinSubCategoryObject=ScriptStruct'/Script/InputCore.Key',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) End Object Begin Object Class=/Script/BlueprintGraph.K2Node_CallFunction Name="K2Node_CallFunction_44" FunctionReference=(MemberParent=Class'/Script/Engine.KismetSystemLibrary',MemberName="QuitGame") NodePosX=992 NodePosY=-832 NodeGuid=F926936F4422AC469B38B3B6EBAB12BE CustomProperties Pin (PinId=BC9E715842362EDD413EF8A39B14F625,PinName="execute",PinToolTip="\nExec",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,LinkedTo=(K2Node_InputKey_0 92DEBC9A43D657DB995617849A0B7927,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=308342D741456EB59283E7A1B0C7183D,PinName="then",PinToolTip="\nExec",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=2EF7797B47E79A543E0AEAB9F7914F98,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target\nKismet System Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'/Script/Engine.KismetSystemLibrary',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,DefaultObject="/Script/Engine.Default__KismetSystemLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=E5668388474ABD86D0C505AE22C65ECB,PinName="WorldContextObject",PinToolTip="World Context Object\nObject Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'/Script/CoreUObject.Object',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=8E90ECF5471A7E7EA51527B95CA238F9,PinName="SpecificPlayer",PinToolTip="Specific Player\nPlayer Controller Object Reference\n\nThe specific player to quit the game. If not specified, player 0 will quit.",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'/Script/Engine.PlayerController',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=FDCA728E4AFA5A2ABEA52A88B5B79FEA,PinName="QuitPreference",PinToolTip="Quit Preference\nEQuitPreference Enum",PinType.PinCategory="byte",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Enum'/Script/Engine.EQuitPreference',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsMap=False,PinType.bIsSet=False,PinType.bIsArray=False,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,DefaultValue="Quit",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) End Object Begin Object Class=/Script/UnrealEd.EdGraphNode_Comment Name="EdGraphNode_Comment_7" NodePosX=816 NodePosY=-880 NodeWidth=490 NodeHeight=230 NodeComment="Exit" NodeGuid=92DC2D0B492925CE0957EB865A535520 End Object
Third Person View
Right after testing the vanilla Third Person Character template in UE you will notice that the camera can be fully moved around the player character.
In Oni the orbiting is limited. IMO this added to Oni's unique vibe. Hand-to-hand combat felt more realistic because you can't really see behind your back.
- Part I
To reproduce the same behavior you have to silent the mouse controls on some conditions.
In theory the core function would hold these parts:
- check for camera's rotation relative to the character (if rotation exceeds limits the input gets nullified)
- check for left turn mouse rotation to get out of camera's right side maximum
- check for right turn mouse rotation to get out of camera's left side maximum
In practice I tweaked the facing value so that is always below 180 degrees (e.g. -270 becomes 90). That way following code only needs the later 2 of 3 checks.
- Part II
During the BeginPlay Event you can set nodes to limit the camera pitch (up and down movement).
- Part III
Oni's second unique feature of its Third Person View is the look mode. It shifts the mouse rotation control from the body to the head.
By that you can look to the side while keep running forward.
- Part IV
The limited mouse controls feel clumsy as long as the character can turn around by pressing the "walk backwards" key.
We need changes so that the character actually walks backwards and does NOT turn around.
Sound files extracted with OniSplit aren't compatible, you have to convert them to 16-bit PCM wav again. Audacity works well for that.
The OSBD properties can be recreated by turning the sound into a cue and then adding BluePrint logic to it.
UE4 is unable to seamlessly loop sound parts which is a requirement for playing permutations. However, almost every Oni modder imports complete soundtracks, so it's not that much of a loss.
Import two wav files to the content browser. Right-click one to "Create Cue". Double-click that created Cue.
Add "Wave Player", "Enveloper", "Random", and "Looping" nodes.
Set a wave file for each new Wave Player.
Select all and press "C" to group them in a comment. That way you better remember what it does and can move the stuff around all at once.
Custom collision boxes
In an 3D editor create collision objects and name it beginning with "UCX_". Save objects and collision objects together in one FBX file.
On import, uncheck "Auto Generate Collision".
Since OniSplit outputs 3D meshes mostly as Collada (DAE) - and few as Wavefront (OBJ) - you cannot transfer objects from Oni to UE4 directly.
Also the scaling is too small. Use a factor of 10 to fix it. If we wanted to do a mass-import we would need a tool or a Blender script that alters the DAE (XML) data in advance. Also boxes do only have 5 sides. Writing a macro to fix that would be more spicy. I'm not sure if it is worth the effort to write one.
Tips and tricks:
- Reset pivot of meshes (V)
You can't socket static meshes. Set them to be "movable".
For now each socket will need its own bool variable to keep track whether it is in use or not.
Problem: when we use this node "get overlapping actors" then also actors stored in sockets will be counted. We would need to subtract the ones from the others. To differentiate the items we could check for a new variable "UsedInSocket". If the number is higher than -1 we can subtract it from the overlapping items because we already have this one collected.
Tested in 4.18
If powerups don't spam the area they should fall to the ground after use. They should be throwable and kickable to attract the attention of foes.
Maybe there should be a weight penalty to motivate h2h combat. As less items you carry around as faster, powerful and agile you are. Reminded to Ultra saiyan vs Super saiyan?
- All powerup "glowtex" textures has been recreated in higher resolution.
- Pickup materials use emissive color to be actual "glowtex".
- Pickup event executes a preflight to check for valid objects. For example it will determine whether your pickup slots for hypos are all full or not. In a second step only one of the valid pickups is added to the inventory.
- Slots in the character skeleton will be used for further experiments. Pickups will be stored in belt and chest pockets. (Short one-hand weapons (e.g. pistols) are going to be holstered at the legs while rifles are put on the back.)
- Powerups in sockets are scaled down and their glowtex becomes disabled.
- If powerups use the same names for their inner components (TV, glowtex plane, item mesh) then the code could maybe be simplified with an "interface blueprint" which take some inputs and gives them to all powerups that are bound to that interface. That would be useful when adding more items and weapons.
In Powerup BP: Custom event with boolean input parameter
- Set Visibility (New Visibility) (target: glowtex Plane object)
- Static Mesh Component-> AddLocalOffset
- Powerup Item -> Add local Rotation, Set World Scale 3D
In order to indicate ammo state of weapon a dynamic material can be used.
In this attempt the glow texture was applied to a plane that is attached to the hypo mesh. Right now the actual hypo mesh is just a placeholder made of a cuboid with green ellipses. The transparency seemed to make it darker than expected so I added some input to the material's emissive color. See gallery below.
In the following there is code that let's you pickup Hyposprays (1 + 2) and use them to regain health (3 + 4).
Caution: This script is different from the others as the the pickup key press event is located in the level blueprint. Normally key press events are found in the the character blueprint. We will probably want to change that in a future version though I don't know how yet. --paradox-01 (talk) 21:03, 16 December 2017 (CET)
If you add a trigger volume to your hypospray object it will act like a new collision box. This can be used to adds some tolerance to the pickup distance. Otherwise the character has to be not just really close but to actually overlap the object.
Code: Press key to pickup hypo
Code: Adds hypo to inventory
Code: Press key to apply hypo
Code: Increases health via hypo
- new hypo object mesh
- pickup animations (kneel and take, take, "roll-take")
- condition to prevent pickup with certain animations
- new sounds ("cannot pickup more", "usage")
- health bar interaction
- flashing animation
- start red health area with a higher health value
- improve code where HypoRegenerationRate adds in to avoid rounding errors
Tested in 4.18
Observation from Oni 1 vanilla health bar. If base health is 200 points...
- remaining health is flashing at 60 points every 2s one time
- remaining health is flashing at 1 point every 1s two times
- critical health sound loop at 39 points (minimum sound volume)
- critical health sound loop at 1 point (max. sound volume)
- health drain animation from 200 to 1 health points takes 5s
- health gain animation speed is determined by HypoRegenerationRate in ONCC
- overpower health gain animation has an overpower sound of an approximate volume of 75% while overpower sound is played at 100% volume once overpower gain ended and drops again
An applied hypo gives you points equal to your full normal health health. At this point of time these points should be considered a "potential" because they aren't multiplied yet with hypo factors and can increase or decrease by additional healing and damage events.
As long as you regenerate normal health you regenerate from your potential multiplied by 0.25 (Oni 1 default).
As soon as you reach overhealth you multiply one point of potential by 1 (Oni 1 default).
Let's say your maximum health is 1000 and with overhealth you are at 2000.
Taking 2 hypos at health 750 will regenerate you 250 points of normal health and then - because reaching overhealth - you get another 1000 points. So taking 2 hypos at 750 health points will let you reach 2000.
A calculation with "potential" give you the advantage to stack hypos more easily.
UE4's progress bar has a fixed range of 0 to 1. In order to realize overpower values (float 1.0 to 2.0) we have to scale everything down to fit into a 0 to 100 percentage range.
If we think of the different colors as milestones on a road whereby each milestone is named red, yellow and so on then each two stones span their own area of interpolated colors. So for UE4's "Lerp (LineraColor)" we would will need several alpha ranges, always float values from 0 to 1.
Health and overhealth points have different densities which results in different grow speeds on the artwork.
To recreate a similar behavior we can let the normals health points fill the health bar with a speed of 1.5 while overpower is the sum of normal health * 1.5 plus health over 100% * 0.5. Since the the health bar isn't a full circle we tweaking the second factor to about 0.44.
- Anywhere from 0 to 20% health the bar is flashing to indicate damage.
- While regenerating the health bar is slowly increasing while a transparent area indicates how much the regeneration will be. That zone is color-interpolated on current health.
- Taken damage is indicated by instantly dropped health while a transparent area is melting to the new health value.
So, both - present and past/future health - use animations.
To deal with the transparent areas it might be the easiest to add another object to the health bar. In total we have then three: a frame texture, a "future/past health" material, and a "present health" material.
How to increase values over time? It looks like "timeline" don't have inputs for start and end time and timers don't work in character BP. Even loops are problematic: if they contain a delay node it is simply ignored. The solution seems to be a custom event. Once triggered it will increase or decrease the health value and call itself again if the certain conditions are still true. The animation seem smooth enough with a 0.1 second delay.
Health bar recreation
There are two options to create a mask: a computed texture (vector) or a real texture designed in a image editor.
For color changes we would need a dynamic material in any case.
In the following sub section there are the required steps to achieve a health display meter.
Basically the mask (shape) of the health display meter.
This will be used as a node inside the material.
Create Material Blueprint, Set output node to Material Domain: User interface (!), create a constant 4 Vector, right-click to turn it into a Parameter and name it Color, connect to output node
- Right-click in assets folder to create a new widget (User Interface > Widget Blueprint).
- Place an image into your Designer. This is going to be the health display meter.
- Under Appearance choose your Material.
Turning the Material in a dynamic one and setting initial color... Create variable of type "Material Instance Dynamic", create 2 vector variables (rgb red, rgb green, use 0 to 1 range). Then in In your widget goto Graph and add these nodes to your Construct and Custom event...
- Construct event -> Get HealthImage, Get Dynamic Material, set Dynamic Material variable, Get Player Character, Cast to ThirdPersonCharacter, Target MaxHealth, Set Progress (call function)
- Custom event -> Set Progress (with a float input), Get Dynamic Material, Set Vector Parameter Value (Parameter name ("Color") from your material), add lerp(linearColor) to SVPV node, add vector color variables to lerp node (here we can add more logic to recreate ONGS' <Color> tags)
Displaying the widget to screen and creating a reference ...
- In your Character BP create or add these to the BeginPlay event node:
- Get Player Controller, Create Widget (your health bar widget), Add to Viewport, set output Create Widget to a public variable (object) (use search to find widget)
Setting progress on the HUD... In Character PB ...
- event ..., get widget object variable, Set Progress (Target: var, Input: Health Percentage)
Any key can be used to open a menu. But when the game gets paused too then pressing the same key again cannot close the menu.
You need to set the key's property "Execute when Paused" to true.
Also, in theory a "Flipflop" node would work but has proven unreliable. Instead, a reference to the menu instead, an "Is Visible" and a "Branch" (if) node will do the trick. Additionally, an Is Valid node is placed to catch an error when the reference is used for the very first time.
We can capture the mouse within the game application if we place an "Set Input Mode Game And UI" node with "Lock Always" option.
Button and section images
Buttons have 4 styles: Normal, Hovered, Pressed, Disabled.
For now lets use only 2 textures: one for Normal, one for Hovered and Pressed.
The Disabled style just uses a tint color on the Normal texture with an RGBA of 000000FF.
Buttons can be disabled via Set Is Enabled (Target is Widget). You can get button references from the Graph window.
When you place a Grid Panel into the widget you can define columns and rows. Buttons and other elements can adapt to their given space.
You probably want to set button texture property Draw As to Image.
For standalone images Draw As Border can be used if the image is big enough. This will work like Oni's XML:PSpc where only specific sections of the image are scaled.
For extreme different resolutions we might need to bind different textures so Draw As Border won't produce empty spaces in the center.
For now I see 3 major flaws with text items:
- no richtext support (you are limited to one font color, unless you use a C++ plugin)
- text warping is not accurately working
- space for an image has to be planned in advance
Text vs text box
Because of the multiline feature I rushed to use a text box for static text.
Reminder: While "Text" is for static text, "Text box" is for letting the user type his own text.
It turns out the font color of "Text box" cannot be changed at runtime and a simple "Text" object does also has the multi-line feature.
Dynamic text content
We can either use
- A) the "Set Text" node with a reference to the object or
- B) bind the object to a text variable or function, making the code a bit shorter
// ------------------------------------ move this to Pause Menu section at a later time
When you insert text you have to compile (F7) before you see the result.
Text pages can be grouped in the Designer with a Widget Switcher. However, each text should have an Overlay as parent so they aren't all shown simultaneously.
-- WidgetSwitcher (master section, e.g. for "Main content") +-- WidgetSwitcher (section, e.g. for powerup items) +-- Overlay +-- Text (e.g. hypospray text) +-- Image (e.g. hypospray icon) -- WidgetSwitcher (master section, e.g. for "hint text") +-- WidgetSwitcher (section, e.g. for powerup item hints) +-- Overlay +-- Text (e.g. hypospray hint text)
Count direct children of WidgetSwitcher by using the its reference connected to a Get Num Widget node.
It's possibly a good idea to count all sections during the construction event and save the results in an array via Make Array node.
In theory we could create many copies of Overlay nodes. But doing so and future update would mean more work than necessary. Instead a single overlay with its children can be do the job by assigning data to them from a data table.
Variables that track active pages are destroyed as the widget is closed. If we want to resume reading after reopening a menu the variable values should have been copied to somewhere else. If we put them into character variable then we can easily restore those values even after a level loading.
HTF do I? Import a CSV File into a Data Table ( UE4 )
- if you need a free spreadsheet program you can download Calc, it is part of LibreOffice, or use Google Sheets
- create a csv file with the first cell of first column (A1) being empty and the other lower cells (A2, A3, ...) to be unique in name
- Calc: save file, check "edit filter settings", save csv with comma as field delimiter
- in ue4 goto "Add New" > Blueprints > Structure
- open that Struct and add for all columns (except for A) a variable, assign a type for each
- be sure Struct's variables have the same name as the csv column headers
- import the csv and assign the Struc to it
- data tabes can be updated by reimporting the csv
for now diary (DPge) will have this structure
- levelNumber = Integer
- pageNumber = Integer
- isLearnedMove = Integer
- caption = String
- image = Texture 2D
- diaryText = String
Stuff that might be useful later...
Then again maybe this will do: https://www.youtube.com/watch?v=e8MPDnVX1h4
If not I will try to just use one text field for diaryText, etc.
If textures aren't found in the data table, open them once from the asset browser. It seems there's a bug which prevents new textures from being registered properly until they were accessed for the first time.
Objective pages are level specific.
Optional small image.
Main and hint section holds green text (todo) and red text (done).
Main section: Small image plus orange text.
- Name .............. Function
Hint section: orange text.
Main section: Small image plus orange text.
- Attribute .......... value
Hint section: orange text.
Diary pages are global -- in a manner that the latest date would get you all other diary pages as well.
- "New move" page: main window for orange caption and a big image. Explanation in the hint section, white text.
- "Regular diary" page: main section only, white text.
Green text for main section and explanation. Hint section not shown. "Controls" Action .............. Mouse/Key
And explanation overlaying the UI.
Every key can be used in blueprints. In order to allow key remapping by the player keys should be mapped in the setting instead of using detecting their real key stroke.
Goto Project Settings > Engine > Input.
Name your action or axis, then bind a key to it.
Requirements: typically a variable in the Character BP. For example you can set "IsPickingUp?" to true with a "Q" button "Pressed" action and setting it to false with the "Released" action.
Then in Character Animation BP > EventGraph there should be a "Event Blueprint Update Animation". There you retrieve "IsPickingUp?" and save it in a local variable. You can retrieve variables from other blueprints by getting the object holding them, passing it to a cast node, pulling a wire out of the right side and search for it in the popping up context menu.
Then in Character Animation BP > Anim Graph > Default State Machine you pull out from Idle/Run a wire and create a new state.
In the transition rule for Idle to Pickup create two nodes: The local variable IsPickingUp and the "Result - Can Enter Transition".
In the transition rule for Pickup to Idle create three nodes: The local variable IsPickingUp, a boolean "NOT" and the "Result - Can Enter Transition".
Now the character plays the animation as long as you press the key. If you want to play the full animation just by pressing the button firmly you have to use a slightly different setup. Use these three nodes instead: "Time Remaining (ratio)"(Animation name), float > float node, Result node.
The float 0 to 1 means how much of the animation will play before it is aborted if the key wasn't longer pressed. In this case, the lower check float the more of the animation is played. Use a number greater than 0 like 0.001
To disable other input you have to create another variable in the Character BP with is also retrieved in the Character Animation BP by casting.
In this example it was named "Movement Allowed". The event that sets and resets the variable was created in the Animation.
Every animation can have Notifies events. Right-click notifies track to add a new custom notify.
Can be created in Photoshop (3D) and Blender.
Level Blueprint, right-click to search for "Keyboard Event V", add that node, drag the "Pressed" slot to the backgound to create another node: "Print String". Compile. Close.
Play Level, hit "V". "Hello" appears on the screen.
Use you imagination: This could be used to toggle visibility of developer stuff. In connection with a HUD you could create the equivalent of "chr_debug_characters = 1"
Cooking the game
On Windows, run the game editor with administration rights.
Then go: File > Package Project > Windows > Windows (64-bit).
Choose an output folder.
You can now play the game.
Exit the game
Create two nodes in level blueprints: "input keyboard escape" and "quit game". Connect the execution slots.
Those videos are merely video proofs that a certain feature can be made by us with this engine. Not more, not less.
- Glowtex can be disabled if we need to. Though I think the majority of the modders wants a non-visible "hammerspace" inventroy.
- The pickup animation from "Character Interaction" is too slow for Oni's fast pace. Also the animation needs tweaking so that it better interpolates to the idle anim.