Jump to content

How infinite terrain can be implemented in Leadwerks Engine 5

Josh

3,180 views

Gamers have always been fascinated with the idea of endless areas to roam.  It seems we are always artificially constrained within a small area to play in, and the possibility of an entire world outside those bounds is tantalizing.  The game FUEL captured this idea by presenting the player with an enormous world that took hours to drive across:

In the past, I always implemented terrain with one big heightmap texture, which had a fixed size like 1024x1024, 2048x2048, etc.  However, our vegetation system, featured in the book Game Engine Gems 3, required a different approach.  There was far too many instances of grass, trees, and rocks to store them all in memory, and I wanted to do something really radical.  The solution was to create an algorithm that could instantly calculate all the vegetation instances in a given area.  The algorithm would always produce the same result, but the actual data would never be saved, it was just retrieved in the area where you needed it, when you needed it.  So with a few modifications, our vegetation system is already set up to generate infinite instances far into the distance.

blogentry-652-12746406290766.thumb.jpg.89e358c7cb0b35eff0f0ab2f6042b3ae.jpg

However, terrain is problematic.  Just because an area is too far away to see doesn't mean it should stop existing.  If we don't store the terrain in memory then how do we prevent far away objects from falling into the ground?  I don't like the idea of disabling far away physics because it makes things very complex for the end user.  There are definitely some tricks we can add like not updating far away AI agents, but I want everything to just work by default, to the best of my ability.

It was during the development of the vegetation system that I realized the MISSING PIECE to this puzzle.  The secret is in the way collision works with vegetation.  When any object moves all the collidable vegetation instances around it are retrieved and collision is performed on this fetched data.  We can do the exact same thing with terrain   Imagine a log rolling across the terrain.  We could use an algorithm to generate all the triangles it potentially could collide with, like in the image below.

Image1.jpg.4d2a3ec26a8aee5b5345884327aa7706.jpg

You can probably imagine how it would be easy to lay out an infinite grid of flat squares around the player, wherever he is standing in the world.

3.jpg.94d1a9006513d292eb923751fee23f90.jpg

What if we only save heightmap data for the squares the user modifies in the editor?  They can't possibly modify the entire universe, so let's just save their changes and make the default terrain flat.  It won't be very interesting, but it will work, right?

What if instead of being flat by default, there was a function we had that would procedurally calculate the terrain height at any point?  The input would be the XZ position in the world and the output would be a heightmap value.

func.jpg.22a8720c464111c0982989a929e24a42.jpg

If we used this, then we would have an entire procedurally generated terrain combined with parts that the developer modifies by hand with the terrain tools.  Only the hand-modified parts would have to be saved to a series of files that could be named "mapname_x_x.patch", i.e. "magickingdom_54_72.patch".  These patches could be loaded from disk as needed, and deleted from memory when no longer in use.

The real magic would be in developing an algorithm that could quickly generate a height value given an XZ position.  A random seed could be introduced to allow us to create an endless variety of procedural landscapes to explore.  Perhaps a large brush could even be used to assign characteristics to an entire region like "mountainy", "plains", etc.

The possibilities of what we can do in Leadwerks Engine 5 are intriguing.  Granted I don't have all the answers right now, but implementing a system like this would be a major step forward that unlocks an enormous world to explore.  What do you think?

5289431185_999629e855_b.jpg.09a5923c412a6027525620715ed6c093.jpg

  • Like 7


26 Comments


Recommended Comments



Infinite terrain would be really cool, but how do you add infinite content to infinite terrain?

It would be wonderful for a sandbox type of game

Share this comment


Link to comment
2 minutes ago, martyj said:

Infinite terrain would be really cool, but how do you add infinite content to infinite terrain?

It would be wonderful for a sandbox type of game

The vegetation can easily be infinite.  Of course you will only have cities and towns here and there, and not everywhere.

So I am pretty sure we can either have infinite or very very large terrains.

  • Like 1

Share this comment


Link to comment
4 minutes ago, Josh said:

The vegetation can easily be infinite.  Of course you will only have cities and towns here and there, and not everywhere.

So I am pretty sure we can either have infinite or very very large terrains.

So one thing I would like, if not impossible, is the ability to hide a specific item in the vegetation layer.

For example:

A player walks up and cuts down a tree.

We could hide that instance of the tree and replace it with an instanced object to perform, say a dropping animation.

Is this currently possible? Would be really useful for infinite based content to have all the vegetation intractable.
 

Share this comment


Link to comment
2 minutes ago, martyj said:

So one thing I would like, if not impossible, is the ability to hide a specific item in the vegetation layer.

For example:

A player walks up and cuts down a tree.

We could hide that instance of the tree and replace it with an instanced object to perform, say a dropping animation.

Is this currently possible? Would be really useful for infinite based content to have all the vegetation intractable.
 

Good idea!  Not exactly supported right now but it could be implemented by modifying the layer mask.

What would even be better is the ability to convert a vegetation instance back into a full entity.  Timber!

  • Like 1

Share this comment


Link to comment

Would that be modifying VegiationLayer::filtermap?

I assume vegetation is defined by an image with the same size as the Terrain's height map. If colored it would show that vegetation. 0 = no possible vegetation?

Updating the vegetation texture would update the vegetation on the GPU? (Haven't looked at any shaders yet)

Share this comment


Link to comment
6 minutes ago, martyj said:

Would that be modifying VegiationLayer::filtermap?

I assume vegetation is defined by an image with the same size as the Terrain's height map. If colored it would show that vegetation. 0 = no possible vegetation?

Updating the vegetation texture would update the vegetation on the GPU? (Haven't looked at any shaders yet)

Yes, I think so.  However, there is not a 1:1 ratio of instances and pixels on the filter texture.  You could have multiple trees that are covered by the same filter pixel, so you would have to actually get all instances that pixel effects and convert them all into entities before masking that pixel out.

  • Like 2

Share this comment


Link to comment

You didn't mention it but your post makes it seem like it'll cover it: multiplayer.  Two players can be in two different parts of the map at the same time.  If you ever want to implement automatic multiplayer synchronization as I think you mentioned a while back, this is something else to keep in mind (and make you scratch your head even more).

Share this comment


Link to comment
19 minutes ago, gamecreator said:

You didn't mention it but your post makes it seem like it'll cover it: multiplayer.  Two players can be in two different parts of the map at the same time.  If you ever want to implement automatic multiplayer synchronization as I think you mentioned a while back, this is something else to keep in mind (and make you scratch your head even more).

Ah, but my proposed design for the physics easily handles this.  Wherever a moving object exists, that heightmap data is loaded, or it is generated procedurally if it does not exist.

  • Like 2

Share this comment


Link to comment

Yay Josh is showing interest in procedural stuff. 

This would be amazing if it comes with an LOD system for normal entities.  Limiting LOD to vegetation makes open world design extra complicated and is one of the main reasons I put hunt for food on hold. 

 

As martyj said:

2 hours ago, martyj said:

Infinite terrain would be really cool, but how do you add infinite content to infinite terrain?

with the current system only your vegetation could be infinite; you will quickly run out of space for real entities on the scene tree and eventually render your project unusable by adding too many entities.

  • Thanks 1

Share this comment


Link to comment
2 minutes ago, Haydenmango said:

Yay Josh is showing interest in procedural stuff. 

This would be amazing if it comes with an LOD system for normal entities.  Limiting LOD to vegetation makes open world design extra complicated and is one of the main reasons I put hunt for food on hold. 

 

As martyj said:

with the current system only your vegetation could be infinite; you will quickly run out of space for real entities on the scene tree and eventually render your project unusable by adding too many entities.

The scene management system is very good at handling large numbers of objects that are outside the visible range.  The Zone was specifically created to test this system, as it has a huge number of objects but many items are very small and have a short view range.

So it's not purely number of objects that will slow the engine down, although Leadwerks 5 in general will give your game loop more time to run before you see a hit in performance.

In any case I am very interested in pushing our limits forward in version 5 and would love to hear any ideas you have.

  • Like 1

Share this comment


Link to comment
5 minutes ago, Josh said:

The scene management system is very good at handling large numbers of objects that are outside the visible range.  The Zone was specifically created to test this system, as it has a huge number of objects but many items are very small and have a short view range.

So it's not purely number of objects that will slow the engine down, although Leadwerks 5 in general will give your game loop more time to run before you see a hit in performance.

I was unaware The Zone was infinite. :P

 

As for suggestions I have this: 

 

Share this comment


Link to comment
3 minutes ago, Haydenmango said:

I was unaware The Zone was infinite. :P

That's what I am saying, it could be.  There can be 100,000 entities outside of the camera range at rest and they will have no impact on performance.

  • Like 1

Share this comment


Link to comment

Very excited that you're considering large-scale terrain for Leadwerks 5, that topic's been an interest of mine for ages. I particularly find the algorithm for "Real-Time Deformable Terrain Rendering with DirectX 11" by Egor Yusov in GPU Pro 3 interesting. They basically observe that terrain at any point on the grid usually only deviates slightly from a value linearly interpolated from its neighbors. They use that to only store the delta from that interpolation and get a data set that can be very efficiently compressed. The terrain is then reconstructed at run time from those delta sets according to the required LOD level. There might be some interesting ideas for you in there (and by now OpenGL also provides the relevant features like texture arrays etc.)

Share this comment


Link to comment
8 hours ago, Rastar said:

Very excited that you're considering large-scale terrain for Leadwerks 5, that topic's been an interest of mine for ages. I particularly find the algorithm for "Real-Time Deformable Terrain Rendering with DirectX 11" by Egor Yusov in GPU Pro 3 interesting. They basically observe that terrain at any point on the grid usually only deviates slightly from a value linearly interpolated from its neighbors. They use that to only store the delta from that interpolation and get a data set that can be very efficiently compressed. The terrain is then reconstructed at run time from those delta sets according to the required LOD level. There might be some interesting ideas for you in there (and by now OpenGL also provides the relevant features like texture arrays etc.)

The same thought has occurred to me.  I will check the book out!  We are moving into an exciting phase of new research and development, and the sky is the limit for Leadwerks 5.  I want your biggest ideas.

Share this comment


Link to comment

Here's another idea I've had on how to get more detail out of the terrain with less points.  If just one point is interpolated then the memory consumption goes down to 25% what it would be otherwise.

Image1.jpg.7272df63684de72a130e725ab0205311.jpg

Share this comment


Link to comment

Hi, for generating terrain, I would not use randomized data. You could use function like voronoi noise that is based on fractals. Then the user data would be a modifier over that generated noise. I think they use an approach similar to this in No Man's sky to generate the planets and the terrain. So if you would be doing a multiplayer game over that terrain, everybody would see the same thing.

  • Like 2

Share this comment


Link to comment

How did you make the roads in this (I found it hard to replicate on the terrain editor in LE):

blogentry-652-12746406290766.jpg.44a3329844844b343c7d1df3a3c6cd02.jpg

Share this comment


Link to comment
20 minutes ago, jen said:

How did you make the roads in this (I found it hard to replicate on the terrain editor in LE):

blogentry-652-12746406290766.jpg.44a3329844844b343c7d1df3a3c6cd02.jpg

I believe that shot is from LE2 from Dave Lee's blog.  I just googled a random image and posted the first one with a lot of trees.

Share this comment


Link to comment
On 12/5/2017 at 2:36 PM, martyj said:

Infinite terrain would be really cool, but how do you add infinite content to infinite terrain?

It would be wonderful for a sandbox type of game

User driven content is the answer and it's what more and more games are moving to. Let your users create the content. Just provide them with terrain and veg. The ability for users to build in game has really taken off and it's only getting stronger.

 

I always thought it was odd how physics is always taking up cycles on objects that can't possibly be hit by anything just by existing. Seems you'd only want a small moving section around something that is moving to check things directly around it. If you gridded out the terrain, a player walking around would only need to check collisions with a 3x3 grid around it with it in the center grid at all times. Dynamically load the 3 grids and unload the other 3 grids as the player moves around. I would think the same ideas of a 3x3 gird could be done for actual terrain chunks. Loading/unloading as they player moves around.

There is no need for things to happen in unloaded terrain chunks in real-time. When one gets loaded it just may needs to know about some state variables so things inside of it can adjust accordingly. Push a button in 1 grid that should cause x to happen in an unloaded grid? That state variable is set (and stays with the player) and when grid x is loaded whatever entity is on it checks that state variable and puts itself in whatever state it needs to show the effect. So terrain state variables become special and whenever a terrain chunk is loaded all entities on it have a method called which has all state variables available to it so they can do what they need to do based on the value of the state variable.

 

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Blog Entries

    • By Josh in Josh's Dev Blog 2
      Previously I talked about the technical details of hardware tessellation and what it took to make it truly useful. In this article I will talk about some of the implications of this feature and the more advanced ramifications of baking tessellation into Turbo Game Engine as a first-class feature in the 
      Although hardware tessellation has been around for a few years, we don't see it used in games that often. There are two big problems that need to be overcome.
      We need a way to prevent cracks from appearing along edges. We need to display a consistent density of triangles on the screen. Too many polygons is a big problem. I think these issues are the reason you don't really see much use of tessellation in games, even today. However, I think my research this week has created new technology that will allow us to make use of tessellation as an every-day feature in our new Vulkan renderer.
      Per-Vertex Displacement Scale
      Because tessellation displaces vertices, any discrepancy in the distance or direction of the displacement, or any difference in the way neighboring polygons are subdivided, will result in cracks appearing in the mesh.

      To prevent unwanted cracks in mesh geometry I added a per-vertex displacement scale value. I packed this value into the w component of the vertex position, which was not being used. When the displacement strength is set to zero along the edges the cracks disappear:

      Segmented Primitives
      With the ability to control displacement on a per-vertex level, I set about implementing more advanced model primitives. The basic idea is to split up faces so that the edge vertices can have their displacement scale set to zero to eliminate cracks. I started with a segmented plane. This is a patch of triangles with a user-defined size and resolution. The outer-most vertices have a displacement value of 0 and the inner vertices have a displacement of 1. When tessellation is applied to the plane the effect fades out as it reaches the edges of the primitive:

      I then used this formula to create a more advanced box primitive. Along the seam where the edges of each face meet, the displacement smoothly fades out to prevent cracks from appearing.

      The same idea was applied to make segmented cylinders and cones, with displacement disabled along the seams.


      Finally, a new QuadSphere primitive was created using the box formula, and then normalizing each vertex position. This warps the vertices into a round shape, creating a sphere without the texture warping that spherical mapping creates.

      It's amazing how tessellation and displacement can make these simple shapes look amazing. Here is the full list of available commands:
      shared_ptr<Model> CreateBox(shared_ptr<World> world, const float width = 1.0); shared_ptr<Model> CreateBox(shared_ptr<World> world, const float width, const float height, const float depth, const int xsegs = 1, const int ysegs = 1); shared_ptr<Model> CreateSphere(shared_ptr<World> world, const float radius = 0.5, const int segments = 16); shared_ptr<Model> CreateCone(shared_ptr<World> world, const float radius = 0.5, const float height = 1.0, const int segments = 16, const int heightsegs = 1, const int capsegs = 1); shared_ptr<Model> CreateCylinder(shared_ptr<World> world, const float radius = 0.5, const float height=1.0, const int sides = 16, const int heightsegs = 1, const int capsegs = 1); shared_ptr<Model> CreatePlane(shared_ptr<World> world, cnst float width=1, const float height=1, const int xsegs = 1, const int ysegs = 1); shared_ptr<Model> CreateQuadSphere(shared_ptr<World> world, const float radius = 0.5, const int segments = 8); Edge Normals
      I experimented a bit with edges and got some interesting results. If you round the corner by setting the vertex normal to point diagonally, a rounded edge appears.

      If you extend the displacement scale beyond 1.0 you can get a harder extended edge.

      This is something I will experiment with more. I think CSG brush smooth groups could be used to make some really nice level geometry.
      Screen-space Tessellation LOD
      I created an LOD calculation formula that attempts to segment polygons into a target size in screen space. This provides a more uniform distribution of tessellated polygons, regardless of the original geometry. Below are two cylinders created with different segmentation settings, with tessellation disabled:

      And now here are the same meshes with tessellation applied. Although the less-segmented cylinder has more stretched triangles, they both are made up of triangles about the same size.

      Because the calculation works with screen-space coordinates, objects will automatically adjust resolution with distance. Here are two identical cylinders at different distances.

      You can see they have roughly the same distribution of polygons, which is what we want. The same amount of detail will be used to show off displaced edges at any distance.

      We can even set a threshold for the minimum vertex displacement in screen space and use that to eliminate tessellation inside an object and only display extra triangles along the edges.

      This allows you to simply set a target polygon size in screen space without adjusting any per-mesh properties. This method could have prevented the problems Crysis 2 had with polygon density. This also solves the problem that prevented me from using tessellation for terrain. The per-mesh tessellation settings I worked on a couple days ago will be removed since it is not needed.
      Parallax Mapping Fallback
      Finally, I added a simple parallax mapping fallback that gets used when tessellation is disabled. This makes an inexpensive option for low-end machines that still conveys displacement.

      Next I am going to try processing some models that were not designed for tessellation and see if I can use tessellation to add geometric detail to low-poly models without any cracks or artifacts.
    • By Josh in Josh's Dev Blog 0
      For finer control over what 2D elements appear on what camera, I have implemented a system of "Sprite Layers". Here's how it works:
      A sprite layer is created in a world. Sprites are created in a layer. Layers are attached to a camera (in the same world). The reason the sprite layer is linked to the world is because the render tweening operates on a per-world basis, and it works with the sprite system just like the entity system. In fact, the rendering thread uses the same RenderNode class for both.
      I have basic GUI functionality working now. A GUI can be created directly on a window and use the OS drawing commands, or it can be created on a sprite layer and rendered with 3D graphics. The first method is how I plan to make the new editor user interface, while the second is quite flexible. The most common usage will be to create a sprite layer, attach it to the main camera, and add a GUI to appear in-game. However, you can just as easily attach a sprite layer to a camera that has a texture render target, and make the GUI appear in-game on a panel in 3D. Because of these different usages, you must manually insert events like mouse movements into the GUI in order for it to process them:
      while true do local event = GetEvent() if event.id == EVENT_NONE then break end if event.id == EVENT_MOUSE_DOWN or event.id == EVENT_MOUSE_MOVE or event.id == EVENT_MOUSE_UP or event.id == EVENT_KEY_DOWN or event.id == EVENT_KEY_UP then gui:ProcessEvent(event) end end You could also input your own events from the mouse position to create interactive surfaces, like in games like DOOM and Soma. Or you can render the GUI to a texture and interact with it by feeding in input from VR controllers.

      Because the new 2D drawing system uses persistent objects instead of drawing commands the code to display elements has changed quite a lot. Here is my current button script. I implemented a system of abstract GUI "rectangles" the script can create and modify. If the GUI is attached to a sprite layer these get translated into sprites, and if it is attached directly to a window they get translated into system drawing commands. Note that the AddTextRect doesn't even allow you to access the widget text directly because the widget text is stored in a wstring, which supports Unicode characters but is not supported by Lua.
      --Default values widget.pushed=false widget.hovered=false widget.textindent=4 widget.checkboxsize=14 widget.checkboxindent=5 widget.radius=3 widget.textcolor = Vec4(1,1,1,1) widget.bordercolor = Vec4(0,0,0,0) widget.hoverbordercolor = Vec4(51/255,151/255,1) widget.backgroundcolor = Vec4(0.2,0.2,0.2,1) function widget:MouseEnter(x,y) self.hovered = true self:Redraw() end function widget:MouseLeave(x,y) self.hovered = false self:Redraw() end function widget:MouseDown(button,x,y) if button == MOUSE_LEFT then self.pushed=true self:Redraw() end end function widget:MouseUp(button,x,y) if button == MOUSE_LEFT then self.pushed = false if self.hovered then EmitEvent(EVENT_WIDGET_ACTION,self) end self:Redraw() end end function widget:OK() EmitEvent(EVENT_WIDGET_ACTION,self) end function widget:KeyDown(keycode) if keycode == KEY_ENTER then EmitEvent(EVENT_WIDGET_ACTION,self) self:Redraw() end end function widget:Start() --Background self:AddRect(self.position, self.size, self.backgroundcolor, false, self.radius) --Border if self.hovered == true then self:AddRect(self.position, self.size, self.hoverbordercolor, true, self.radius) else self:AddRect(self.position, self.size, self.bordercolor, true, self.radius) end --Text if self.pushed == true then self:AddTextRect(self.position + iVec2(1,1), self.size, self.textcolor, TEXT_CENTER + TEXT_MIDDLE) else self:AddTextRect(self.position, self.size, self.textcolor, TEXT_CENTER + TEXT_MIDDLE) end end function widget:Draw() --Update position and size self.primitives[1].position = self.position self.primitives[1].size = self.size self.primitives[2].position = self.position self.primitives[2].size = self.size self.primitives[3].size = self.size --Update the border color based on the current hover state if self.hovered == true then self.primitives[2].color = self.hoverbordercolor else self.primitives[2].color = self.bordercolor end --Offset the text when button is pressed if self.pushed == true then self.primitives[3].position = self.position + iVec2(1,1) else self.primitives[3].position = self.position end end This is arguably harder to use than the Leadwerks 4 system, but it gives you advanced capabilities and better performance that the previous design did not allow.
    • By reepblue in reepblue's Blog 0
      As you may have known, I've been dabbling with input methods for a while now using SDL2. Since then, I've learned how to do similar functions using the Leadwerks API. The goal was to make a inout system that's easily re-bindable, and allows for controllers to "just work". My first research of a goof system comes from a talk at Steam DevDays 2016 as they discuss how to allow integration with the Steam Controller. 
       
      My thought was: "If I can create my own Action System, I can bind any controller with any API I want". The SDL experiments was a result of this, but they ended up being sloppy when you tried to merge the window polling from SDL into Leadwerks.
      The next goal was to remove SDL2 out of the picture. I've created functions to allow reading and simulations of button presses with the Leadwerks Window class.
      //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool InputSystem::KeyHit(const int keycode) { auto window = GetActiveEngineWindow(); if (keycode < 7) return window->MouseHit(keycode); return window->KeyHit(keycode); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool InputSystem::KeyDown(const int keycode) { auto window = GetActiveEngineWindow(); if (window != NULL) { if (keycode < 7) return window->MouseDown(keycode); return window->KeyDown(keycode); } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void InputSystem::SimulateKeyHit(const char keycode) { auto window = GetActiveEngineWindow(); if (window != NULL) { if (keycode < 7) window->mousehitstate[keycode] = true; window->keyhitstate[keycode] = true; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void InputSystem::SimulateKeyDown(const char keycode) { auto window = GetActiveEngineWindow(); if (window != NULL) { if (keycode < 7) window->mousedownstate[keycode] = true; window->keydownstate[keycode] = true; } } The simulate keys are very important for controllers. for this case, we would trick the window class thinking a key was pressed on the keyboard. The only direct input we would need from the controller is the value analog sticks which I haven't touch as of yet.
       Using JSON, we can load and save our bindings in multiple Action Sets!
      { "keyBindings": { "actionStates": { "Menu": { "selectActive": 1, "selectDown": 40, "selectLeft": 37, "selectRight": 39, "selectUp": 38 }, "Walking": { "crouch": 17, "firePrimary": 1, "fireSecondary": 2, "flashLight": 70, "interact": 69, "jump": 32, "moveBackward": 83, "moveForward": 87, "moveLeft": 65, "moveRight": 68, "reloadWeapon": 82 } } } } You may want a key to do something different when your game is in a certain state. For this example, when the Active Action Set is set to "Menu", Only KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, and KEY_LBUTTON will work. You can still hover over your buttons with the mouse, but when it comes time to implement the controller for example, you'd just call GetActionHit(L"selectActive") to select the highlighted/active button. If the state is set to walking, then all those keys for Menu gets ignored in-favor of the walking commands. All keys/buttons are flushed between switching states!
      Here's example code of this working. "Interact" gets ignored when "Menu" is set as the default action and vise-versa.
      while (window->KeyDown(KEY_END) == false and window->Closed() == false) { if (window->KeyHit(KEY_TILDE)) { if (InputSystem::GetActiveActionSet() == L"Menu") { SetActionSet(L"Walking"); } else { SetActionSet(L"Menu"); } } // Under "Menu" if (GetActionHit(L"selectUp")) { DMsg("selectUp!"); } // Under "Walking" if (GetActionHit(L"interact")) { DMsg("interact!"); } } Only things I didn't implement as of yet is actual controller support and saving changes to the json file which might need to be game specific. I also want to wait until the UI is done before I decide how to do this.
      As for controllers, we can use SteamInput, but what if your game isn't on Steam? You can try to implement XInput yourself if you want. I tried to add Controller support with SDL2, but people reported issues. And then, what about VR controllers? What matters right now is that we have room to add these features later on. All I need to do is use the GetActionHit/Down Commands and the rest doesn't matter.
×
×
  • Create New...