xyz Yes, currently there is no movement path. The player currently has a shape cast that they cast .9 meters in the direction they want to move. Currently after each move_and_slide the player is snapped to a float value position that goes into 1 evenly that is created based on the players speed. So, the move_and_slide moves the character a set amount each frame, and there is a timer that equals 1/speed float value.

So.

If nothing is .9 in direction of movement, move.

If moving set timer to equal 1/speed float value.
Each frame reduce timer by 1
Move_and_slide
Snap vector3 to value of 1/speed float value.
Once timer = 0 reset movement
And snap to vector3 with a value of 1

  • xyz replied to this.

    xRegnarokx A bit too complicated for a mere grid movement but it can work. Where's the actual problem?

      xyz I guess the "a bit too complicated for a mere grid movement" haha. Once I have multiple entities moving around I am worried it will create larger problems with how complicated it is, also shapecasting for moving to a location is unreliable if two objects want to move into the same area at the same time they won't detect collisions until they do, that will force them to either overlap, or stop not in a grid_position. So, if I make the raycasts longer to detect the "next" move increment, that doesn't account for coming from another angle into the same space...

      I am just running into a bottleneck on how to implement a movement that locks everything to a x,y of 1 meter, while allowing for the z index to be handled by physics, rather than manually changing it as well (though that may be what I have to do). If I don't use move_and_slide is there a reliable way that I can get the z index of the terrain, so that I can maybe interpolate the position, ignoring physics interactions and setting up an occupied tile array for positions? I guess the moment the z index comes into the picture in a game that I am trying to emulate psuedo 2D it makes it complicated for me to navigate movement.

      I considered setting up like an Astar movement alongside a gridmap and using that as a base, with the array that stores occupied tiles, however, I am running into how to implement an Astar in 3D, I've implemented it in 2D several times, along with my own very basic custom movement algorithms. I am struggling how to get the Vector3, and if I get them is it the top of the object or the middle, or bottom...

      • xyz replied to this.
        • Edited

        xRegnarokx You're overcomplicating this on several levels.
        First, if your movement is grid based, then you need a custom data structure that holds the grid data, including occupancy info. I think I already suggested that. Doing shapecasts (i.e. physical collisions) is antithetic to grid organization. The whole purpose of having a grid system is to not need to do map collisions. A grid cell can either be occupied or not. It's a logical state, not a matter of colliders it the scene. Once you establish such structure, it'll be much easier to handle movement. Some impassable areas may not even be detected by collisions (e.g. bodies of water). So implement a logical cell occupancy data structure first.

        Next, implement "instant" movement from cell to cell, that checks for occupancy in the occupancy map and handles the positioning onto cell coordinates properly.

        Next, interpolate that instant movement using tweens or manual lerping in _process().

        When you have this in place, we can talk about how to handle unit overlaps. It can be done with the above system without any complicated additions.

          xyz I guess the part that is the part I'm struggling to consider is creating the occupancy data structure. You recommended A* which I am familiar with in 2D. My confusion is setting up the grid structure, implementing lerp/tween.

          Using A* can I eventually have more data stored in points for different movement type, water, rough terrain, ect... or do I need to build a custom grid data structure storing other information? If that is the case would it be sufficient to use a a* to create all the points then run the points through a custom structure adding the additional information? Or should I just start and build my own data structure.

          The biggest barrier I have is how to I get the point coordinance for the cells that == the top of each cell?

          • xyz replied to this.
            • Edited

            xRegnarokx I don't think I recommended A* in your latest batch of questions. A* has nothing to do with movement per se. It's a path finding algorithm. For simple cell to cell movement you don't need any such fancy stuff. You just need a 2D array the size of your gird, populated with boolean values that signify if a cell is occupied or not. That's all there is to a basic occupancy structure. Later you can upgrade it so that each cell holds a terrain type or whatever other cell info. But for basic occupancy, you just need to store one bit of information per cell.

              xyz Okay, I don't know if I'm communicating my barrier. How do I get the Vector3 for moving to a cell? With the Z index I'm not sure how to handle that.

              • xyz replied to this.
                • Edited

                xRegnarokx Your barrier is that you've set up your system too complicated and painted yourself in the corner with it. I'm telling you to re-do your system so that doesn't happen.

                You also ask specific questions without describing your setup precisely. How do you get Vector3 for moving to a cell from where? Is the map flat? Is it a heightfield? Are you using GridMap? Etc.

                In general you need to implement a function that calculates a world position from a grid position and vice versa. Similar to tilemap's map_to_local() and local_to_map(). For that you need to know your grid unit size and your grid origin. Calculation then simply boils down to translating the input position so that both spaces's origns match and multiplying/dividing it with cell size. If your grid and your world origin coincide, then you only need to multiply/divide with the cell size.

                  xyz Sorry for not sufficiently describing my setup. The reason is because I am trying to visualize how to move forward without what I have for movement because like you said it is very complicated. Currently I move without calculating a specific coordinate, but just moving by 1 meter increments. However, if I want to implement an occupancy data structure that will not work well, and also, for using tween or lerp that doesn't work, it requires physics to keep z on the grid.

                  So, I have no setup yet because I am trying to figure out how to move forward. The problem is figuring out how to get the Vector3 before I can move forward with a more pure grid movement. So, how do I create a data structure of cells that have the Vector3 of the cells that I can use for finding the information of the cells, as well as moving via lerp or tweens.

                  I think I can figure out x,y locations to create a calculation between world coords and local. However, if I use gridmap for example, if I have tiles that have different heights how do I figure out their z index coord. Or do I just need to standardize all the heights and put them in different layers/gridmaps that each have their own z index height?

                  • xyz replied to this.
                    • Edited

                    xRegnarokx What is z index you're referring to? In 3d, you don't need any z index. You can simply have continuous elevations.

                    You should first describe your map. How is each cell defined via its logical properties (terrain, elevation etc) and then how you plan to represent that visually. Then we can discuss how to reflect that in your custom grid structure and how it can all be turned into movement in 3d space.

                    Or better yet, give a reference to an existing game that's similar to what you're trying to do.

                      xyz Okay, I guess I am not understanding what you mean by continuous elevations. If you have a cell at z index 0 and one at 2 how would you handle if you wanted jump off the ledge to get to the 0 tile, or if you have stair tiles that halfway up would be at z index 1.

                      I guess I am inspired a bit by an mmorpg called Tibia, at least it gave me the idea for the perspective. I believe is is 2D but with a perspective that gives it more depth. Also, in it you can climb on stacked objects to climb onto ledges.

                      • xyz replied to this.
                        • Edited

                        xRegnarokx Oh so there's vertical movement as well? You should have mentioned that from the start. Best to post some gameplay/movement footage.

                        I googled Tibia gameplay videos. All movement there appears to be happening on the floor plane.

                          xyz Yeah, it is since it is 2D there isn't an actual z axis in tibia, they simulate it though where items can get stacked, and players can climb onto stacked items.

                          Here in the first few seconds you see someone standing on a barrel in the bottom left in the building. I know in 2D this is smoke and mirrors, but I wasn't able to figure out the perspective challenges in godots 2D engine. The sorting characters behind objects both on the x and y axis.
                          [

                          So, I want to try and emulate that in 3d, but that is granted more difficult. But, I am confident I can figure out what I need if I can find a way to get the z index of the top of terrain. Objects are easy, but the tiles on the gridmap are harder.

                          • xyz replied to this.
                            • Edited

                            xRegnarokx You should start by modeling your map with data. Introduce a cell class that contains relevant cell data

                            class Cell:
                            	var z: int = 0 # how many stacks, your "z index"
                            	var walkable: bool = true
                            	# add more stuff later

                            Have a "2D" array of such cells that represents your map:

                            var map: Array[Cell]

                            The array is 1D (since there are no multidimensional arrays in GDScript) but you can implement 2d access functions so it'll work like a 2D array/map:

                            func get_map_cell(map_coord: Vector2i) -> Cell:
                            	# stuff
                            
                            func set_map_cell(map_coord: Vector2i, cell: Cell) -> void:
                            	# stuff

                            That's your basic map data structure. You can do a lot with it.

                              xyz I am slightly confused is this for implementing with godot in 3D or in 2D?

                              Because if this is 2D I am unsure how this would help with my original problem that caused you to suggest I move to 3D. Which was I can y-sort for objects that are "above" however there isn't a good way of handling when you are suppose to be in front of the object on the x-axis but are still above it on the y-axis.

                              If it is 3D, I still am unsure if I understand how this would help me with finding the z index for movement purposes. How is my character if I am not using the build in physics to know where they should be on the z index.

                              I could use the gridmap get_used_cell_by_item() use the item id to manually via code set each items relative z index. Or, I could just create two gridmaps for the two heights of 1 meter and 2 meter.

                              Example:

                              Class Cell:
                                   var elevation : int #sets the items height based on 2 meter increments, each 2 meters is a new elevation
                                   var z_index : int # this would be set at either 0 or 1

                              So, at elevation 0 there would be only tiles at 0 height and 1 meter. So, 0 would be what you'd walk on, and then there would be terrain you could climb on which would be 1 meter. Your player would store his relative elevation, and whenever he moved up 2 meters his elevation would change, so thus while navigating and checking cells based on coordinates they'd all be searched based on their z index at their elevation, but then when going to move to them it would be based on their var z_index to know if you can walk normal, or need to hop up on them.

                              Do you think this would be a logical way to go about this? I would just have to make sure that all blocks that can be walked on are exactly 1 or 2 meters high.

                              • xyz replied to this.
                                • Edited

                                xRegnarokx It can be either. Your map has certain characteristics that should be described through data structures. This is called modeling with data and is essential for developing any kind of software system. I tried to do this in a most rudimentary (and incomplete) form, following your (incomplete) description and what is shown in the video. This is the part that is separate from actual visual representation which can be either 2d or 3d. For this part of code, looks don't matter. When you model your game world with data, you need to primarily think in this semi-abstract way, which doesn't really have anything to do with 2d or 3d or Godot for that matter. It only has to do with your game world. Looks are built on top of it, or based on it.

                                • Edited

                                xRegnarokx If it is 3D, I still am unsure if I understand how this would help me with finding the z index for movement purposes. How is my character if I am not using the build in physics to know where they should be on the z index.

                                There's literally z property in the cell data which holds the integer number of stacks. If you know the height of one stack, multiply z with that height and you've got the vertical position in the world. It's basically a simple version of heightfield.

                                If you compare z of the current cell and z of the cell you want to go to, you'll know if you need to climb, descend or go straight. Or if a cell is an obstacle that is not climbable etc.. All this can and should determine how you animate the traversal.

                                  xyz Ahh okay, so you'd set the z based on its height then you'd be able to find it using Vector2i, which would just ignore the z index. I think I was confused since you had Vector2i, I wasn't thinking about the fact you could set up a grid like that, where the z index is stored separately to enable reliable cell discovery. I am still wrapping my mind around doing things in 3D and coming from 2D the z has been throwing me off.

                                  Thank you so much! You have always pointed me in a good direction. I'll leave this open until I can create some test code to see if I am able to utilize these principles.

                                  • xyz replied to this.
                                    • Edited

                                    xRegnarokx Note that z is perhaps a wrong name to use here. Height would be much more appropriate as it's not linked to any coordinate system. In Godot's 3d the convention is that a ground plane is in x and z directions and up is in y direction.

                                    Here's a more elaborate "sketch" of how the whole map class could look like. Don't just copy paste this. Might not work. It's just to show the way of thinking/organizing.

                                    class_name Map extends RefCounted
                                    
                                    # cell data struct
                                    class Cell:
                                    	var terrain: String = "grass"
                                    	var height: int = 0
                                    	var walkable: bool = true
                                    	func _init(iterrain: String, iheight: int, iwalakble: bool = true): # object constructor
                                    		terrain = iterrain
                                    		height = iheight
                                    		walkable = iwalakble
                                    	
                                    # map size (number of cells)
                                    var _size: Vector2i
                                    
                                    # map
                                    var _cells: Array[Cell]
                                    
                                    # pyisical cell size
                                    var _cell_size_physical: float = 1.0 # xz size of a cell in world units
                                    var _cell_height_pyhsical: float = 1.0 # height of a cell in world units
                                    
                                    # initialize the size of array that holds cell data
                                    func initialize(size: Vector2i):
                                    	_size = size
                                    	_cells.resize(_size.x * _size.y)
                                    	
                                    # returns 1D array index from 2D cell coords, depending on map size 
                                    # used to access a 1D array as if it was a 2D array
                                    func _get_array_index(pos: Vector2i) -> int:
                                    	assert(pos.x >= 0 and pos.x < _size.x and pos.y >= 0 and pos.y < _size.y) # boundary check
                                    	return pos.x + pos.y * _size.y
                                    	
                                    func get_cell(pos: Vector2i) -> Cell:
                                    	return _cells[_get_array_index(pos)]
                                    	
                                    func set_cell(pos: Vector2i, data: Cell) -> void:
                                    	var cell = get_cell(pos)
                                    	cell.terrain = data.terrain
                                    	cell.height = data.height
                                    	cell.walkable = data.walkable
                                    	
                                    # some test utility function
                                    func add_wall(pos: Vector2i) -> void:
                                    	set_cell(pos, Cell.new("wall", 1, false))
                                    	
                                    func add_barrel(pos: Vector2i) -> void:
                                    	set_cell(pos, Cell.new("barral", 1, true))
                                    	
                                    # etc...

                                    Now you can use some generation algorithm to generate an actual maze or map layout into this structure. And when you have that, you can proceed to make a visual representation of it in either 2d using fake ortho perspective or in 3d using actual geometry.

                                    If you prefer visual editing of the map, You can make an actual GridMap with some tiles etc... and write an utility script that interprets what's in the GridMap and populates your data structure.

                                    Alternatively you could implement the same data structure as an extension of a GridMap node, making it more coupled with actual 3D graphics, but the way of thinking about and organizing map data would be exactly the same. You need a "logical" structure that's "underneath" the visual representation.

                                    The movement comes after this, because the movement system needs this for proper functioning. You can't really have a grid based movement system without some data in a grid 😃

                                      xyz Awesome, I appreciate the example. I try not to copy and paste, unless there is something I'm using as a template and am planning on rewriting anyway. I find writing the code myself helps me understand better what is going on.

                                      This makes sense, so, if I do the visual design -> data structure I will create a system to translate the visual fluff to actual useable information in the map data.

                                      So, since I want to go visual design -> map data route, I also need to think through how I am going to translate the gridmap visuals into the cell/map data.