On the Docket for Today:
My plan for this post was pretty simple:
- Generate 3D world from tile map.
- Spawn in the player
- Get player "moving"
To start I needed a class to house all of the code dealing directly with the 3D world, so I created a new C# class called PhysicalWorld. In the PhysicalWorld I placed a reference to the tile map, an object to act as parent to the tile objects, a reference to a player game object, and a TerrainTile.
The first thing the PhysicalWorld needed to be able to do was read the tile map I so lovingly created.
Reading the Map
The method I decided to go with for reading the TileMap was a simple, and quite a familiar one. Once again I am looping through the grid using that same modulo fueled loop that I've used in the FillMap function explained in my last post. Where this loop differs is the offset used for creating the newX and newZ variables, and what it does with each tile it come across.
My first draft of the GenerateWorld function.
The offset in the other looping functions uses half the width of a tile so that each tile will be placed exactly a tile width apart. In GenerateWorld I change the offset so that instead of being based on the width of a 2D tile texture, it would be based on half the width of the 3D model. This would place the models next to each other instead of overlapping.
For each tile that is read, the loop grabs the TerrainTile class attached to the tile and stores it in the PhysicalWorlds _currTerrainTile. Now that I have the TerrainTile at a tile location I can see which texture models are associated with it.
Laying the Sod
Once the _currTerrainTile was set I called a new function BuildGround and passed in the tile's location.
Builds a model from the _currTerrainTile at a given location.
BuildGround takes in a location and uses that location to create the 3D model that is in _currTerrainTile. Now a TerrainTile has an array to hold models, which means that I need to specify which of the models I want to use. I do have 2 models for the grass, one solid green tile and one with a couple of rocks. I originally had the function grab a random prefab using a Random.Range with the length of the models array as the upper boundary. This ended up too cluttered so for while I was still developing the function I hard coded it to only use the first model in the array.
Time to Test !
After getting the ground models spawned in and the tile map disabled I could see a nice grassy field 32x32. But something looked a little ..off. The plane seemed roughly the same size as the tile map, which didn't make sense because the models were bigger than the 2D tiles. I found the problem after selecting 2 different tiles, they were overlapping !
Because of incorrect positioning, the models ended up instantiating overlapped.
This issue had me stumped for a little while. Because of the way I have my model>model_object>prefab
structure for each model it got pretty confusing. It's probably for the best that I ended up doing this, even if it took me more time than I had planned on because it made me really examine how I was accessing the models and textures. I noticed an error with how the PhysicalWorld accessed the model's renderer and in fixing that noticed the real problem.
Earlier when I had changed the looping code to use the size of the offset, I forgot to also change the code from determining the size of the tile to determining the size of the model.
Updated reference vectors tile and offset.
Moving on down the list
The Plan:
Generate 3D world from tile map.- Spawn in the player
- Get player "moving"
The Hero
To get the player created in the world I created another function inside the PhysicalWorld called SpawnPlayer. This new function will handle creating the player data object, and a player game object that will be the visuals.
The SpawnPlayer function.
I created an empty game object (playerGO), named it "Player_Character", and set it's parent to be the PhysicalWorld. Then I created a capsule to act as a placeholder for the character model. I set the capsule's parent to be the playerGO and offset it by the size of the tile model divided by 2. I also offset the capsule by 1.0f in the y axis to place it on top of the tiles instead of on the same level.
Hero Info
Now that I have the playerGO and a temporary model set up I need to add the controller to it, then create a new Player data class to hold all of the player information. I initialize the Player class with the enum Player.SKILL_LOADOUT.NEW_PLAYER
. This is an enum I made so I could more easily create players with specific skill load outs.
I am currently only worrying about the combat stats.
With the player data populated, I could add it to the PlayerController by calling the InitPlayer function and passing along the player data object. The final step in spawning in the player character was to strap the CameraPivot to the player.
To do this I wrote a small function in the CameraStrap class to set the transform the camera pivots on to a passed in transform. I then called this function at the end of SpawnPlayer and passed along the playerGO's transform.
One Last Point Left
Updated Plan:
Generate 3D world from tile map.Spawn in the player- Get player "moving"
Moving Around
I already had a "player" spawned and "moving" in my original post but now I needed to adapt that to my new world generation. I started by adding the MouseController as a component on the playerGO, and initialize it, passing through a reference to the PhysicalWorld and the playerGO's PlayerController.
Adding the MouseController and initializing it with the PlayerController and PhysicalWorld.
The MouseController will one day handle logic around input regarding the mouse. I set it up only deal with left clicks for now, just to get the character moving. I chose to put player movement on the left click because that's how it's done in Old School RuneScape, so that's how it's done in NaughtScape !
A Ray is cast out, returns the collider that it hits which is set to the PlayerControllers destination.
In the PlayerController I have a MoveToDestination function that gets called in the Update function if the destination is different from the currentTileCoord. MoveToDestination calls a Vector3.Lerp()
on the currentTileCoord, destination, and use the Time.deltaTime
as the time parameter.
It's alright movement for now, but in the future I'll be replacing it with an A* pathfinding algorithm.
Until Next Time !
If you have any suggestions for improvements, questions, or links to resources that would help me out feel free to leave a comment. I am using Unity as a game engine and Blender for any 3D modeling I may have to do during this project. Upvote if you liked the post or found it helpful, and follow me for future updates on NaughtScape, sometimes other programming, and occasionally other stuff. All of the source code can be found on my Github.
You could also add me in game on OSRS if you play !
My ign is MapleThunder.
Congratulations @maple-thunder, this post is the forth most rewarded post (based on pending payouts) in the last 12 hours written by a User account holder (accounts that hold between 0.1 and 1.0 Mega Vests). The total number of posts by User account holders during this period was 2546 and the total pending payments to posts in this category was $2246.80. To see the full list of highest paid posts across all accounts categories, click here.
If you do not wish to receive these messages in future, please reply stop to this comment.
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
Woo! You got a curie vote! Way to go!
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit