Refactoring now functional
parent
fcc2fdb8d3
commit
a37b028b39
|
@ -36,7 +36,6 @@ public class Game : Spatial
|
|||
|
||||
// Resources
|
||||
private PackedScene _tileHighlightScene;
|
||||
private TileInstanceManager _tileInstanceManager;
|
||||
private ShaderMaterial _tileMaterial;
|
||||
private Label _tileOffsetLabel;
|
||||
private World _world;
|
||||
|
@ -46,7 +45,7 @@ public class Game : Spatial
|
|||
public override void _Ready()
|
||||
{
|
||||
// debugStatsContainer
|
||||
var debugStatsContainer = (Container)FindNode("DebugStatsContainer");
|
||||
Container debugStatsContainer = (Container)FindNode("DebugStatsContainer");
|
||||
|
||||
_framesPerSecondLabel = debugStatsContainer.GetNode<Label>("fps_label");
|
||||
_centerLabel = debugStatsContainer.GetNode<Label>("center_label");
|
||||
|
@ -60,7 +59,7 @@ public class Game : Spatial
|
|||
_numCoordsRemovedLabel = debugStatsContainer.GetNode<Label>("num_coords_removed_label");
|
||||
|
||||
// UI elements
|
||||
var worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer");
|
||||
Container worldGeneratorContainer = (Container)FindNode("WorldGeneratorContainer");
|
||||
_worldTextureRect = worldGeneratorContainer.GetNode<TextureRect>("WorldTextureRect");
|
||||
_heightTextureRect = worldGeneratorContainer.GetNode<TextureRect>("HeightTextureRect");
|
||||
_generateWorldButton = worldGeneratorContainer.GetNode<Button>("WorldGenerateButton");
|
||||
|
@ -77,10 +76,9 @@ public class Game : Spatial
|
|||
_cameraOffset = _camera.GlobalTranslation - _player.GlobalTranslation;
|
||||
|
||||
_world = (World)FindNode("World");
|
||||
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
|
||||
|
||||
// populate UI values
|
||||
var generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
|
||||
Slider generatorWorldSizeSlider = worldGeneratorContainer.GetNode<Slider>("HBoxContainer/WorldSizeSlider");
|
||||
|
||||
// resources
|
||||
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
|
||||
|
@ -88,7 +86,7 @@ public class Game : Spatial
|
|||
Debug.Assert(_tileMaterial != null);
|
||||
|
||||
_blackWhitePatternTexture = new ImageTexture();
|
||||
var image = new Image();
|
||||
Image image = new Image();
|
||||
image.Load("assets/4x4checker.png");
|
||||
_blackWhitePatternTexture.CreateFromImage(image, (uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
||||
|
||||
|
@ -104,8 +102,8 @@ public class Game : Spatial
|
|||
_player.TaskQueueComponent.Connect("StartInteraction", _interactionSystem,
|
||||
nameof(_interactionSystem.OnStartInteraction));
|
||||
_player.Connect("GoldCountChanged", this, nameof(OnGoldCountChanged));
|
||||
_tileInstanceManager.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||
_tileInstanceManager.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||
_world.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||
_world.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||
_world.Connect("OnWorldViewTileTypeImageChanged", this, nameof(OnWorldViewTileTypeImageChanged));
|
||||
_world.Connect("OnHeightmapImageChanged", this, nameof(OnHeightmapImageChanged));
|
||||
|
||||
|
@ -118,7 +116,7 @@ public class Game : Spatial
|
|||
|
||||
// perform dependency injection
|
||||
//_streamContainer.SetWorld(_tileWorld);Clicked
|
||||
var worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
|
||||
WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
|
||||
|
||||
UpdateCurrentTile();
|
||||
}
|
||||
|
@ -134,9 +132,9 @@ public class Game : Spatial
|
|||
public void UpdateCurrentTile()
|
||||
{
|
||||
// cast a ray from the camera to center
|
||||
var cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
|
||||
var cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
|
||||
var cameraDir = cameraNormal - cameraPosition;
|
||||
Vector3 cameraNormal = _camera.ProjectRayNormal(_camera.GetViewport().Size * 0.5f);
|
||||
Vector3 cameraPosition = _camera.ProjectRayOrigin(_camera.GetViewport().Size * 0.5f);
|
||||
Vector3 cameraDir = cameraNormal - cameraPosition;
|
||||
|
||||
Vector3 centerCoord;
|
||||
|
||||
|
@ -166,13 +164,13 @@ public class Game : Spatial
|
|||
|
||||
UpdateCurrentTile();
|
||||
|
||||
var tileHighlightTransform = Transform.Identity;
|
||||
var currentTileCenter = _hexGrid.GetHexCenter(_currentTile);
|
||||
Transform tileHighlightTransform = Transform.Identity;
|
||||
Vector2 currentTileCenter = _hexGrid.GetHexCenter(_currentTile);
|
||||
tileHighlightTransform.origin.x = currentTileCenter.x;
|
||||
tileHighlightTransform.origin.z = currentTileCenter.y;
|
||||
_tileHighlight.Transform = tileHighlightTransform;
|
||||
|
||||
var cameraTransform = _camera.Transform;
|
||||
Transform cameraTransform = _camera.Transform;
|
||||
cameraTransform.origin = _player.GlobalTranslation + _cameraOffset;
|
||||
_camera.Transform = cameraTransform;
|
||||
}
|
||||
|
@ -181,7 +179,7 @@ public class Game : Spatial
|
|||
public void OnGenerateButton()
|
||||
{
|
||||
GD.Print("Generating");
|
||||
var worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
|
||||
Slider worldSizeSlider = (Slider)FindNode("WorldSizeSlider");
|
||||
if (worldSizeSlider == null) GD.PrintErr("Could not find WorldSizeSlider!");
|
||||
}
|
||||
|
||||
|
@ -189,8 +187,8 @@ public class Game : Spatial
|
|||
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
|
||||
int shapeIndex)
|
||||
{
|
||||
var cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
|
||||
var highlightTransform = Transform.Identity;
|
||||
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
|
||||
Transform highlightTransform = Transform.Identity;
|
||||
|
||||
_mouseWorldLabel.Text = position.ToString("F3");
|
||||
_mouseTileOffsetLabel.Text = cellAtCursor.OffsetCoords.ToString("N");
|
||||
|
@ -213,7 +211,7 @@ public class Game : Spatial
|
|||
|
||||
public void OnTileHovered(HexTile3D tile)
|
||||
{
|
||||
var highlightTransform = tile.GlobalTransform;
|
||||
Transform highlightTransform = tile.GlobalTransform;
|
||||
_mouseTileHighlight.Transform = highlightTransform;
|
||||
_mouseWorldLabel.Text = highlightTransform.origin.ToString("F3");
|
||||
_mouseTileOffsetLabel.Text = tile.OffsetCoords.ToString("N");
|
||||
|
@ -226,7 +224,7 @@ public class Game : Spatial
|
|||
{
|
||||
GD.Print("Clicked on entity at " + entity.GlobalTranslation);
|
||||
|
||||
var mountPoint = (Spatial)entity.FindNode("MountPoint");
|
||||
Spatial mountPoint = (Spatial)entity.FindNode("MountPoint");
|
||||
if (mountPoint != null)
|
||||
{
|
||||
_player.TaskQueueComponent.Reset();
|
||||
|
@ -239,7 +237,7 @@ public class Game : Spatial
|
|||
|
||||
public void ResetGameState()
|
||||
{
|
||||
var playerStartTransform = Transform.Identity;
|
||||
Transform playerStartTransform = Transform.Identity;
|
||||
playerStartTransform.origin.y = 0;
|
||||
_player.Transform = playerStartTransform;
|
||||
_player.TaskQueueComponent.Reset();
|
||||
|
@ -250,9 +248,9 @@ public class Game : Spatial
|
|||
|
||||
foreach (Spatial entity in GetNode("Entities").GetChildren())
|
||||
{
|
||||
var entityTransform = entity.Transform;
|
||||
var entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
|
||||
var entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
|
||||
Transform entityTransform = entity.Transform;
|
||||
Vector2 entityPlanePos = new Vector2(entityTransform.origin.x, entityTransform.origin.z);
|
||||
Vector2 entityOffsetCoordinates = _hexGrid.GetHexAt(entityPlanePos).OffsetCoords;
|
||||
entityTransform.origin.y = 0;
|
||||
entity.Transform = entityTransform;
|
||||
}
|
||||
|
@ -260,7 +258,7 @@ public class Game : Spatial
|
|||
|
||||
private void OnHeightmapImageChanged(Image heightmapImage)
|
||||
{
|
||||
var newHeightmapTexture = new ImageTexture();
|
||||
ImageTexture newHeightmapTexture = new ImageTexture();
|
||||
newHeightmapTexture.CreateFromImage(heightmapImage,
|
||||
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
||||
|
||||
|
@ -269,7 +267,7 @@ public class Game : Spatial
|
|||
|
||||
private void OnWorldViewTileTypeImageChanged(Image viewTileTypeImage)
|
||||
{
|
||||
var newWorldTexture = new ImageTexture();
|
||||
ImageTexture newWorldTexture = new ImageTexture();
|
||||
newWorldTexture.CreateFromImage(viewTileTypeImage,
|
||||
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
||||
|
||||
|
@ -283,7 +281,7 @@ public class Game : Spatial
|
|||
|
||||
public void OnGoldCountChanged(int goldCount)
|
||||
{
|
||||
var animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
|
||||
AnimationPlayer animationPlayer = _gameUi.GetNode<AnimationPlayer>("AnimationPlayer");
|
||||
_goldCountLabel.Text = goldCount.ToString();
|
||||
animationPlayer.CurrentAnimation = "FlashLabel";
|
||||
animationPlayer.Seek(0);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[gd_scene load_steps=22 format=2]
|
||||
[gd_scene load_steps=21 format=2]
|
||||
|
||||
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=2]
|
||||
[ext_resource path="res://scenes/Camera.tscn" type="PackedScene" id=3]
|
||||
|
@ -7,7 +7,6 @@
|
|||
[ext_resource path="res://ui/DebugStatsContainer.gd" type="Script" id=6]
|
||||
[ext_resource path="res://scenes/World.cs" type="Script" id=7]
|
||||
[ext_resource path="res://scenes/Game.cs" type="Script" id=9]
|
||||
[ext_resource path="res://scenes/TileInstanceManager.cs" type="Script" id=10]
|
||||
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=11]
|
||||
[ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12]
|
||||
[ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13]
|
||||
|
@ -358,6 +357,7 @@ flip_v = true
|
|||
visible = false
|
||||
|
||||
[node name="Camera" parent="." instance=ExtResource( 3 )]
|
||||
transform = Transform( 1, 0, 0, 0, 0.60042, 0.799685, 0, -0.799685, 0.60042, -4.76837e-07, 5.16505, 3.1696 )
|
||||
|
||||
[node name="InteractionSystem" type="Node" parent="."]
|
||||
script = ExtResource( 15 )
|
||||
|
@ -370,33 +370,27 @@ WorldNode = NodePath("../World")
|
|||
[node name="WorldInfo" parent="Player" index="2"]
|
||||
WorldPath = NodePath("../../World")
|
||||
|
||||
[node name="ToolAttachement" parent="Player/Geometry/Armature/Skeleton" index="5"]
|
||||
transform = Transform( 1, 8.68458e-08, -1.04308e-07, 1.74623e-07, -1, -1.30385e-07, 1.41561e-07, 1.50874e-07, -1, -0.72, 0.45, 3.28113e-08 )
|
||||
[node name="ToolAttachement" parent="Player/Geometry/PirateAsset/Armature/Skeleton" index="5"]
|
||||
transform = Transform( 1, 7.13626e-08, -4.47035e-08, 1.64262e-07, -1, -1.00583e-07, 1.19209e-07, 1.18278e-07, -1, -0.72, 0.45, 1.78362e-08 )
|
||||
|
||||
[node name="AnimationTree" parent="Player/Geometry" index="2"]
|
||||
parameters/playback = SubResource( 26 )
|
||||
|
||||
[node name="Entities" type="Spatial" parent="."]
|
||||
visible = false
|
||||
|
||||
[node name="Axe" parent="Entities" instance=ExtResource( 14 )]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.79762, 0, 0 )
|
||||
input_ray_pickable = false
|
||||
|
||||
[node name="Chest" parent="Entities" instance=ExtResource( 11 )]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, -3.27709, 0, 1.02593 )
|
||||
transform = Transform( -0.825665, 0, 0.56416, 0, 1, 0, -0.56416, 0, -0.825665, -3.27709, 0, 1.02593 )
|
||||
|
||||
[node name="World" type="Spatial" parent="."]
|
||||
script = ExtResource( 7 )
|
||||
|
||||
[node name="Chunks" type="Spatial" parent="World"]
|
||||
|
||||
[node name="TileInstanceManager" type="Spatial" parent="World"]
|
||||
script = ExtResource( 10 )
|
||||
ShowHexTiles = true
|
||||
World = NodePath("..")
|
||||
|
||||
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World/TileInstanceManager"]
|
||||
[node name="TileMultiMeshInstance" type="MultiMeshInstance" parent="World"]
|
||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 )
|
||||
multimesh = SubResource( 27 )
|
||||
|
||||
|
@ -430,4 +424,4 @@ directional_shadow_mode = 0
|
|||
[connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"]
|
||||
|
||||
[editable path="Player"]
|
||||
[editable path="Player/Geometry"]
|
||||
[editable path="Player/Geometry/PirateAsset"]
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
public class TileInstanceManager : Spatial
|
||||
{
|
||||
// exports
|
||||
[Export] public NodePath World;
|
||||
[Export] public bool ShowHexTiles;
|
||||
[Export] public Vector2 ViewCenterPlaneCoord;
|
||||
|
||||
// scene nodes
|
||||
public MultiMeshInstance TileMultiMeshInstance;
|
||||
|
||||
// other members
|
||||
private readonly Array<SceneTileChunk> _sceneTileChunks = new();
|
||||
private int _usedTileInstanceIndices;
|
||||
private ImageTexture _viewTileTypeTexture;
|
||||
private World _world;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
public override void _Ready()
|
||||
{
|
||||
_world = GetNode<World>(World);
|
||||
|
||||
_world.Connect("OnTilesChanged", this, nameof(HandleWorldTileChange));
|
||||
|
||||
TileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
||||
Debug.Assert(TileMultiMeshInstance != null);
|
||||
}
|
||||
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
}
|
||||
|
||||
private SceneTileChunk CreateSceneTileChunk(Vector2 chunkIndex)
|
||||
{
|
||||
var sceneTileChunk =
|
||||
new SceneTileChunk(chunkIndex, TileMultiMeshInstance, _usedTileInstanceIndices, ShowHexTiles);
|
||||
_usedTileInstanceIndices += sceneTileChunk.TileNodes.Count;
|
||||
|
||||
foreach (var hexTile3D in sceneTileChunk.TileNodes)
|
||||
{
|
||||
hexTile3D.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||
hexTile3D.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||
}
|
||||
|
||||
return sceneTileChunk;
|
||||
}
|
||||
|
||||
private SceneTileChunk FindSceneTileChunkAtIndex(Vector2 chunkIndex)
|
||||
{
|
||||
foreach (Spatial child in GetChildren())
|
||||
{
|
||||
var sceneTileChunk = child as SceneTileChunk;
|
||||
if (sceneTileChunk == null) continue;
|
||||
|
||||
if (sceneTileChunk.ChunkIndex == chunkIndex) return sceneTileChunk;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private void HandleWorldTileChange(Array<Vector2> removedChunkIndices, Array<Vector2> addedChunkIndices)
|
||||
{
|
||||
Array<SceneTileChunk> removedChunks = new();
|
||||
foreach (var chunkIndex in removedChunkIndices)
|
||||
{
|
||||
var chunk = FindSceneTileChunkAtIndex(chunkIndex);
|
||||
if (chunk != null) removedChunks.Add(chunk);
|
||||
}
|
||||
|
||||
foreach (var chunkIndex in addedChunkIndices)
|
||||
{
|
||||
SceneTileChunk sceneTileChunk = null;
|
||||
if (removedChunks.Count > 0)
|
||||
{
|
||||
sceneTileChunk = removedChunks[^1];
|
||||
sceneTileChunk.ChunkIndex = chunkIndex;
|
||||
removedChunks.RemoveAt(removedChunks.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sceneTileChunk = CreateSceneTileChunk(chunkIndex);
|
||||
AddChild(sceneTileChunk);
|
||||
}
|
||||
|
||||
_sceneTileChunks.Add(sceneTileChunk);
|
||||
}
|
||||
|
||||
GD.Print("Removed Chunks " + removedChunkIndices.Count);
|
||||
GD.Print("Added Chunks " + addedChunkIndices.Count);
|
||||
GD.Print("Removed chunk count: " + removedChunks.Count);
|
||||
}
|
||||
|
||||
public void OnTileClicked(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileClicked", tile);
|
||||
}
|
||||
|
||||
public void OnTileHovered(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileHovered", tile);
|
||||
}
|
||||
|
||||
// signals
|
||||
[Signal]
|
||||
private delegate void TileClicked(HexTile3D tile3d);
|
||||
|
||||
[Signal]
|
||||
private delegate void TileHovered(HexTile3D tile3d);
|
||||
|
||||
private class SceneTileChunk : Spatial
|
||||
{
|
||||
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
||||
private readonly MultiMeshInstance _multiMeshInstance;
|
||||
private readonly Array<int> _tileInstanceIndices = new();
|
||||
private readonly HexGrid _hexGrid = new();
|
||||
private readonly bool _showHexTiles;
|
||||
|
||||
public readonly Array<HexTile3D> TileNodes = new();
|
||||
private Vector2 _chunkIndex = Vector2.Inf;
|
||||
|
||||
|
||||
public SceneTileChunk(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance, int tileInstanceIndexStart,
|
||||
bool showHexTiles)
|
||||
{
|
||||
_showHexTiles = showHexTiles;
|
||||
|
||||
var tileInstanceIndexStart1 = tileInstanceIndexStart;
|
||||
var chunkSize = global::World.ChunkSize;
|
||||
|
||||
foreach (var i in Enumerable.Range(0, chunkSize))
|
||||
foreach (var j in Enumerable.Range(0, chunkSize))
|
||||
{
|
||||
var tile3D = (HexTile3D)_hexTile3DScene.Instance();
|
||||
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * global::World.ChunkSize + new Vector2(i, j));
|
||||
|
||||
var tileTransform = Transform.Identity;
|
||||
var centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
|
||||
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
|
||||
tile3D.Transform = tileTransform;
|
||||
|
||||
TileNodes.Add(tile3D);
|
||||
AddChild(tile3D);
|
||||
}
|
||||
|
||||
_multiMeshInstance = multiMeshInstance;
|
||||
|
||||
var chunkTileCount = global::World.ChunkSize * global::World.ChunkSize;
|
||||
|
||||
Debug.Assert(tileInstanceIndexStart1 + chunkTileCount <= _multiMeshInstance.Multimesh.InstanceCount);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, chunkTileCount))
|
||||
_tileInstanceIndices.Add(tileInstanceIndexStart1 + i);
|
||||
|
||||
// _multiMeshInstance.Multimesh.InstanceCount += chunkTileCount;
|
||||
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
||||
|
||||
ChunkIndex = chunkIndex;
|
||||
}
|
||||
|
||||
public Vector2 ChunkIndex
|
||||
{
|
||||
get => _chunkIndex;
|
||||
|
||||
set
|
||||
{
|
||||
var chunkTransform = Transform.Identity;
|
||||
var chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(value * global::World.ChunkSize);
|
||||
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
|
||||
Transform = chunkTransform;
|
||||
_chunkIndex = value;
|
||||
|
||||
var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f);
|
||||
|
||||
GD.Print("Updating transforms for instances of chunk " + value + " origin: " + chunkTransform.origin);
|
||||
|
||||
foreach (var i in Enumerable.Range(0, _tileInstanceIndices.Count))
|
||||
{
|
||||
var column = i % global::World.ChunkSize;
|
||||
var row = i / global::World.ChunkSize;
|
||||
|
||||
var tilePlaneCoord =
|
||||
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
||||
|
||||
var hexTransform = new Transform(tileOrientation,
|
||||
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
|
||||
|
||||
if (_showHexTiles)
|
||||
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
|
||||
hexTransform.origin);
|
||||
|
||||
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
296
scenes/World.cs
296
scenes/World.cs
|
@ -17,7 +17,7 @@ public class World : Spatial
|
|||
}
|
||||
|
||||
// constants
|
||||
public const int ChunkSize = 18;
|
||||
public const int ChunkSize = 12;
|
||||
public const int NumChunkRows = 3;
|
||||
public const int NumChunkColumns = NumChunkRows;
|
||||
private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f);
|
||||
|
@ -25,11 +25,13 @@ public class World : Spatial
|
|||
private static readonly Color DarkGrassColor = new(0.05882353f, 0.5411765f, 0.05882353f);
|
||||
private static readonly Color LightWaterColor = new(0.05882353f, 0.05882353f, 0.8627451f);
|
||||
|
||||
private readonly List<Vector2> _addedChunkIndices = new();
|
||||
private readonly Godot.Collections.Dictionary<Vector2, WorldChunk> _cachedWorldChunks;
|
||||
private readonly List<Vector2> _addedChunkIndices = new();
|
||||
private readonly List<WorldChunk> _unusedWorldChunks = new();
|
||||
private readonly Image _heightmapImage = new();
|
||||
private readonly List<Vector2> _removedChunkIndices = new();
|
||||
private readonly Image _tileTypeMapImage = new();
|
||||
private int FrameCounter;
|
||||
|
||||
// referenced scenes
|
||||
private readonly PackedScene _worldChunkScene = GD.Load<PackedScene>("res://scenes/WorldChunk.tscn");
|
||||
|
@ -48,7 +50,8 @@ public class World : Spatial
|
|||
|
||||
private OpenSimplexNoise _noiseGenerator = new();
|
||||
private Array<Spatial> _rockAssets;
|
||||
private TileInstanceManager _tileInstanceManager;
|
||||
private MultiMeshInstance _tileMultiMeshInstance;
|
||||
private int _usedTileMeshInstances;
|
||||
private Array<Spatial> _treeAssets;
|
||||
private ImageTexture _viewTileTypeTexture;
|
||||
public Vector2 CenterChunkIndex = Vector2.Zero;
|
||||
|
@ -83,6 +86,13 @@ public class World : Spatial
|
|||
[Signal]
|
||||
public delegate void EntityClicked(Entity entity);
|
||||
|
||||
// signals
|
||||
[Signal]
|
||||
private delegate void TileClicked(HexTile3D tile3d);
|
||||
|
||||
[Signal]
|
||||
private delegate void TileHovered(HexTile3D tile3d);
|
||||
|
||||
public World()
|
||||
{
|
||||
Debug.Assert(ChunkSize % 2 == 0);
|
||||
|
@ -96,10 +106,11 @@ public class World : Spatial
|
|||
Chunks = (Spatial)FindNode("Chunks");
|
||||
Debug.Assert(Chunks != null);
|
||||
|
||||
_tileInstanceManager = (TileInstanceManager)FindNode("TileInstanceManager");
|
||||
Debug.Assert(_tileInstanceManager != null);
|
||||
_tileInstanceManager.TileMultiMeshInstance.Multimesh.InstanceCount =
|
||||
_tileMultiMeshInstance = (MultiMeshInstance)FindNode("TileMultiMeshInstance");
|
||||
Debug.Assert(_tileMultiMeshInstance != null);
|
||||
_tileMultiMeshInstance.Multimesh.InstanceCount =
|
||||
ChunkSize * ChunkSize * NumChunkColumns * NumChunkRows;
|
||||
_usedTileMeshInstances = 0;
|
||||
|
||||
InitNoiseGenerator();
|
||||
|
||||
|
@ -128,65 +139,72 @@ public class World : Spatial
|
|||
_noiseGenerator.Lacunarity = 2;
|
||||
}
|
||||
|
||||
public WorldChunk GetOrCreateWorldChunk(int xIndex, int yIndex, Color debugColor)
|
||||
public WorldChunk GetOrCreateWorldChunk(Vector2 chunkIndex, Color debugColor)
|
||||
{
|
||||
if (IsChunkCached(xIndex, yIndex))
|
||||
WorldChunk chunk;
|
||||
|
||||
if (IsChunkCached(chunkIndex))
|
||||
return _cachedWorldChunks[chunkIndex];
|
||||
|
||||
if (_unusedWorldChunks.Count > 0)
|
||||
{
|
||||
var cachedChunk = _cachedWorldChunks[new Vector2(xIndex, yIndex)];
|
||||
return cachedChunk;
|
||||
chunk = _unusedWorldChunks.First();
|
||||
_unusedWorldChunks.RemoveAt(0);
|
||||
|
||||
GD.Print("Reusing chunk from former index " + chunk.ChunkIndex + " at new index " + chunkIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk = CreateWorldChunk(chunkIndex, debugColor);
|
||||
}
|
||||
|
||||
return CreateWorldChunk(xIndex, yIndex, debugColor);
|
||||
_cachedWorldChunks[chunkIndex] = chunk;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private bool IsChunkCached(int xIndex, int yIndex)
|
||||
private bool IsChunkCached(Vector2 chunkIndex)
|
||||
{
|
||||
return _cachedWorldChunks.ContainsKey(new Vector2(xIndex, yIndex));
|
||||
return _cachedWorldChunks.ContainsKey(chunkIndex);
|
||||
}
|
||||
|
||||
private WorldChunk CreateWorldChunk(int xIndex, int yIndex, Color debugColor)
|
||||
|
||||
private WorldChunk CreateWorldChunk(Vector2 chunkIndex, Color debugColor)
|
||||
{
|
||||
var result = (WorldChunk)_worldChunkScene.Instance();
|
||||
WorldChunk result = (WorldChunk)_worldChunkScene.Instance();
|
||||
Chunks.AddChild(result);
|
||||
result.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||
result.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||
|
||||
result.SetSize(ChunkSize);
|
||||
result.InitializeTileInstances(chunkIndex, _tileMultiMeshInstance, _usedTileMeshInstances);
|
||||
_usedTileMeshInstances += result.Tiles.GetChildCount();
|
||||
|
||||
var offsetCoordSouthWest = new Vector2(xIndex, yIndex) * ChunkSize;
|
||||
var offsetCoordNorthEast = offsetCoordSouthWest + new Vector2(1, 1) * (ChunkSize - 1);
|
||||
|
||||
var planeCoordSouthWest = HexGrid.GetHexCenterFromOffset(offsetCoordSouthWest) +
|
||||
new Vector2(-HexGrid.HexSize.x, HexGrid.HexSize.y) * 0.5f;
|
||||
var planeCoordNorthEast = HexGrid.GetHexCenterFromOffset(offsetCoordNorthEast) +
|
||||
new Vector2(HexGrid.HexSize.x, -HexGrid.HexSize.y) * 0.5f;
|
||||
|
||||
result.ChunkIndex = new Vector2(xIndex, yIndex);
|
||||
result.PlaneRect = new Rect2(
|
||||
new Vector2(planeCoordSouthWest.x, planeCoordNorthEast.y),
|
||||
new Vector2(planeCoordNorthEast.x - planeCoordSouthWest.x, planeCoordSouthWest.y - planeCoordNorthEast.y));
|
||||
result.SetChunkIndex(chunkIndex, HexGrid);
|
||||
result.UpdateTileTransforms();
|
||||
|
||||
result.DebugColor = debugColor;
|
||||
result.DebugColor.a = 0.6f;
|
||||
|
||||
Chunks.AddChild(result);
|
||||
var chunkIndex = new Vector2(xIndex, yIndex);
|
||||
_cachedWorldChunks.Add(chunkIndex, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private bool IsColorEqualApprox(Color colorA, Color colorB)
|
||||
{
|
||||
var colorDifference = new Vector3(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
|
||||
Vector3 colorDifference = new(colorA.r - colorB.r, colorA.g - colorB.g, colorA.b - colorB.b);
|
||||
return colorDifference.LengthSquared() < 0.1 * 0.1;
|
||||
}
|
||||
|
||||
private Spatial SelectAsset(Vector2 offsetCoord, Array<Spatial> assets, Random randomGenerator, double probability)
|
||||
private Spatial SelectAsset(Vector2 textureCoord, Array<Spatial> assets, Random randomGenerator, double probability)
|
||||
{
|
||||
if (randomGenerator.NextDouble() < 1.0 - probability) return null;
|
||||
|
||||
var assetIndex = randomGenerator.Next(assets.Count);
|
||||
var assetInstance = (Spatial)assets[assetIndex].Duplicate();
|
||||
var assetTransform = Transform.Identity;
|
||||
assetTransform.origin = HexGrid.GetHexCenterVec3FromOffset(offsetCoord);
|
||||
int assetIndex = randomGenerator.Next(assets.Count);
|
||||
Spatial assetInstance = (Spatial)assets[assetIndex].Duplicate();
|
||||
Transform assetTransform = Transform.Identity;
|
||||
assetTransform.origin = HexGrid.GetHexCenterVec3FromOffset(textureCoord);
|
||||
// TODO: assetTransform.origin.y = GetHeightAtOffset(offsetCoord);
|
||||
assetTransform.origin.y = 0;
|
||||
assetTransform.basis =
|
||||
|
@ -198,30 +216,30 @@ public class World : Spatial
|
|||
|
||||
private void PopulateChunk(WorldChunk chunk)
|
||||
{
|
||||
var environmentRandom = new Random(Seed);
|
||||
Random environmentRandom = new(Seed);
|
||||
|
||||
var tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData();
|
||||
Image tileTypeImage = chunk.TileTypeOffscreenViewport.GetTexture().GetData();
|
||||
tileTypeImage.Lock();
|
||||
|
||||
foreach (var textureCoordU in Enumerable.Range(0, chunk.Size))
|
||||
foreach (var textureCoordV in Enumerable.Range(0, chunk.Size))
|
||||
foreach (int textureCoordU in Enumerable.Range(0, chunk.Size))
|
||||
foreach (int textureCoordV in Enumerable.Range(0, chunk.Size))
|
||||
{
|
||||
var colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV);
|
||||
var textureCoord = new Vector2(textureCoordU, textureCoordV);
|
||||
var offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
|
||||
Color colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV);
|
||||
Vector2 textureCoord = new(textureCoordU, textureCoordV);
|
||||
Vector2 offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord;
|
||||
|
||||
if (IsColorEqualApprox(colorValue, RockColor))
|
||||
{
|
||||
var rockAsset = SelectAsset(offsetCoord, _rockAssets, environmentRandom, 0.15);
|
||||
Spatial rockAsset = SelectAsset(textureCoord, _rockAssets, environmentRandom, 0.15);
|
||||
if (rockAsset != null) chunk.Entities.AddChild(rockAsset);
|
||||
// TODO: MarkCellUnwalkable(cell);
|
||||
}
|
||||
else if (IsColorEqualApprox(colorValue, GrassColor) || IsColorEqualApprox(colorValue, DarkGrassColor))
|
||||
{
|
||||
var grassAsset = SelectAsset(offsetCoord, _grassAssets, environmentRandom, 0.15);
|
||||
Spatial grassAsset = SelectAsset(textureCoord, _grassAssets, environmentRandom, 0.15);
|
||||
if (grassAsset != null) chunk.Entities.AddChild(grassAsset);
|
||||
|
||||
Tree treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.05) as Tree;
|
||||
Tree treeAsset = SelectAsset(textureCoord, _treeAssets, environmentRandom, 0.05) as Tree;
|
||||
if (treeAsset != null)
|
||||
{
|
||||
chunk.Entities.AddChild(treeAsset);
|
||||
|
@ -268,83 +286,89 @@ public class World : Spatial
|
|||
return;
|
||||
}
|
||||
|
||||
GD.Print("Update Chunks: " + FrameCounter);
|
||||
|
||||
// mark all chunks as retired
|
||||
Godot.Collections.Dictionary<Vector2, WorldChunk> oldCachedChunks = new(_cachedWorldChunks);
|
||||
|
||||
// set new center chunk
|
||||
var chunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
|
||||
CenterChunkIndex = new Vector2(chunkIndex.Item1, chunkIndex.Item2);
|
||||
CenterChunkIndex = GetChunkTupleFromPlaneCoord(planeCoord);
|
||||
|
||||
var currentChunk = GetOrCreateWorldChunk(chunkIndex.Item1, chunkIndex.Item2,
|
||||
WorldChunk currentChunk = GetOrCreateWorldChunk(CenterChunkIndex,
|
||||
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
|
||||
_centerChunkRect = currentChunk.PlaneRect;
|
||||
|
||||
|
||||
_centerChunkRect = new Rect2(
|
||||
new Vector2(currentChunk.Transform.origin.x, currentChunk.Transform.origin.z)
|
||||
+ currentChunk.PlaneRect.Position,
|
||||
currentChunk.PlaneRect.Size);
|
||||
GD.Print("Center Chunk Rect: " + _centerChunkRect.Position + " size: " + _centerChunkRect.Size);
|
||||
|
||||
// load or create adjacent chunks
|
||||
_activeChunkIndices = new List<Vector2>();
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 - 1));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 - 1));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 - 1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, -1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(0, -1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(1, -1));
|
||||
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, 0));
|
||||
_activeChunkIndices.Add(CenterChunkIndex);
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, 0));
|
||||
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 - 1, chunkIndex.Item2 + 1));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1, chunkIndex.Item2 + 1));
|
||||
_activeChunkIndices.Add(new Vector2(chunkIndex.Item1 + 1, chunkIndex.Item2 + 1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(-1, +1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(0, +1));
|
||||
_activeChunkIndices.Add(CenterChunkIndex + new Vector2(+1, +1));
|
||||
|
||||
// clear unused chunks
|
||||
_unusedWorldChunks.Clear();
|
||||
_addedChunkIndices.Clear();
|
||||
|
||||
foreach (Vector2 oldChunkIndex in oldCachedChunks.Keys)
|
||||
if (!_activeChunkIndices.Contains(oldChunkIndex))
|
||||
DeactivateChunk(oldCachedChunks[oldChunkIndex]);
|
||||
|
||||
foreach (Vector2 activeChunkIndex in _activeChunkIndices)
|
||||
{
|
||||
WorldChunk chunk = GetOrCreateWorldChunk(activeChunkIndex,
|
||||
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
|
||||
_cachedWorldChunks[activeChunkIndex] = chunk;
|
||||
}
|
||||
|
||||
Debug.Assert(_activeChunkIndices.Count == NumChunkRows * NumChunkColumns);
|
||||
|
||||
foreach (var activeChunkIndex in _activeChunkIndices)
|
||||
GetOrCreateWorldChunk((int)activeChunkIndex.x, (int)activeChunkIndex.y,
|
||||
new Color(GD.Randf(), GD.Randf(), GD.Randf()));
|
||||
|
||||
// unload retired chunks
|
||||
_removedChunkIndices.Clear();
|
||||
_addedChunkIndices.Clear();
|
||||
|
||||
foreach (var cachedChunkKey in oldCachedChunks.Keys)
|
||||
if (!_activeChunkIndices.Contains(cachedChunkKey))
|
||||
RemoveChunk(cachedChunkKey);
|
||||
|
||||
foreach (var chunkKey in _activeChunkIndices)
|
||||
foreach (Vector2 chunkKey in _activeChunkIndices)
|
||||
if (!oldCachedChunks.ContainsKey(chunkKey))
|
||||
{
|
||||
_addedChunkIndices.Add(chunkKey);
|
||||
|
||||
var chunk = _cachedWorldChunks[chunkKey];
|
||||
GenerateChunkNoiseMap(chunk);
|
||||
|
||||
ActivateChunk(_cachedWorldChunks[chunkKey], chunkKey);
|
||||
State = GenerationState.Heightmap;
|
||||
}
|
||||
}
|
||||
|
||||
private void ActivateChunk(WorldChunk chunk, Vector2 chunkIndex)
|
||||
{
|
||||
chunk.SetChunkIndex(chunkIndex, HexGrid);
|
||||
chunk.UpdateTileTransforms();
|
||||
|
||||
_addedChunkIndices.Add(chunk.ChunkIndex);
|
||||
GD.Print("Generating noise for chunk " + chunk.ChunkIndex);
|
||||
GenerateChunkNoiseMap(chunk);
|
||||
}
|
||||
|
||||
private void DeactivateChunk(WorldChunk chunk)
|
||||
{
|
||||
GD.Print("Clearing chunk index: " + chunk.ChunkIndex);
|
||||
_cachedWorldChunks.Remove(chunk.ChunkIndex);
|
||||
chunk.ClearContent();
|
||||
_unusedWorldChunks.Add(chunk);
|
||||
}
|
||||
|
||||
private void GenerateChunkNoiseMap(WorldChunk chunk)
|
||||
{
|
||||
var chunkIndex = chunk.ChunkIndex;
|
||||
Vector2 chunkIndex = chunk.ChunkIndex;
|
||||
|
||||
var debugChunkColor = new Color(Mathf.Abs(chunkIndex.x) / 5, Mathf.Abs(chunkIndex.y) / 5,
|
||||
Mathf.RoundToInt(Mathf.Abs(chunkIndex.x + chunkIndex.y)) % 2);
|
||||
|
||||
var noiseImageTexture = new ImageTexture();
|
||||
ImageTexture noiseImageTexture = new();
|
||||
noiseImageTexture.CreateFromImage(_noiseGenerator.GetImage(ChunkSize, ChunkSize, chunkIndex * ChunkSize),
|
||||
0);
|
||||
|
||||
// Debug Texture
|
||||
var simpleImage = new Image();
|
||||
simpleImage.Create(ChunkSize, ChunkSize, false, Image.Format.Rgb8);
|
||||
simpleImage.Lock();
|
||||
|
||||
foreach (var i in Enumerable.Range(0, ChunkSize))
|
||||
foreach (var j in Enumerable.Range(0, ChunkSize))
|
||||
if ((i + j) % 2 == 0)
|
||||
simpleImage.SetPixelv(new Vector2(i, j), Colors.Aqua);
|
||||
else
|
||||
simpleImage.SetPixelv(new Vector2(i, j), debugChunkColor);
|
||||
|
||||
simpleImage.Unlock();
|
||||
// noiseImageTexture.CreateFromImage(simpleImage, 0);
|
||||
|
||||
chunk.SetNoisemap(noiseImageTexture);
|
||||
}
|
||||
|
||||
|
@ -359,12 +383,10 @@ public class World : Spatial
|
|||
}
|
||||
|
||||
|
||||
private Tuple<int, int> GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
|
||||
private Vector2 GetChunkTupleFromPlaneCoord(Vector2 planeCoord)
|
||||
{
|
||||
var centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
|
||||
var chunkIndexFloat = (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
|
||||
var chunkIndex = new Tuple<int, int>((int)chunkIndexFloat.x, (int)chunkIndexFloat.y);
|
||||
return chunkIndex;
|
||||
HexCell centerOffsetCoord = HexGrid.GetHexAt(planeCoord);
|
||||
return (centerOffsetCoord.OffsetCoords / ChunkSize).Floor();
|
||||
}
|
||||
|
||||
public void SetCenterPlaneCoord(Vector2 centerPlaneCoord)
|
||||
|
@ -381,18 +403,18 @@ public class World : Spatial
|
|||
|
||||
private void UpdateWorldViewTexture()
|
||||
{
|
||||
var worldChunkSize = ChunkSize;
|
||||
var numWorldChunkRows = NumChunkRows;
|
||||
var numWorldChunkColumns = NumChunkColumns;
|
||||
int worldChunkSize = ChunkSize;
|
||||
int numWorldChunkRows = NumChunkRows;
|
||||
int numWorldChunkColumns = NumChunkColumns;
|
||||
|
||||
_heightmapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
|
||||
Image.Format.Rgba8);
|
||||
_tileTypeMapImage.Create(worldChunkSize * numWorldChunkColumns, worldChunkSize * numWorldChunkRows, false,
|
||||
Image.Format.Rgba8);
|
||||
|
||||
foreach (var chunkIndex in _activeChunkIndices)
|
||||
foreach (Vector2 chunkIndex in _activeChunkIndices)
|
||||
{
|
||||
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
|
||||
WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White);
|
||||
|
||||
_heightmapImage.BlendRect(
|
||||
worldChunk.HeightmapOffscreenViewport.GetTexture().GetData(),
|
||||
|
@ -422,9 +444,9 @@ public class World : Spatial
|
|||
_chunkIndexSouthWest = Vector2.Inf;
|
||||
_chunkIndexNorthEast = -Vector2.Inf;
|
||||
|
||||
foreach (var chunkIndex in _activeChunkIndices)
|
||||
foreach (Vector2 chunkIndex in _activeChunkIndices)
|
||||
{
|
||||
var worldChunk = GetOrCreateWorldChunk((int)chunkIndex.x, (int)chunkIndex.y, Colors.White);
|
||||
WorldChunk worldChunk = GetOrCreateWorldChunk(chunkIndex, Colors.White);
|
||||
|
||||
if (chunkIndex.x <= _chunkIndexSouthWest.x && chunkIndex.y <= _chunkIndexSouthWest.y)
|
||||
_chunkIndexSouthWest = chunkIndex;
|
||||
|
@ -435,71 +457,79 @@ public class World : Spatial
|
|||
|
||||
private void UpdateNavigationBounds()
|
||||
{
|
||||
var cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize);
|
||||
HexCell cellSouthWest = HexGrid.GetHexAtOffset(_chunkIndexSouthWest * ChunkSize);
|
||||
// Chunks have their cells ordered from south west (0,0) to north east (ChunkSize, ChunkSize). For the
|
||||
// north east cell we have to add the chunk size to get to the actual corner cell.
|
||||
var cellNorthEast = HexGrid.GetHexAtOffset(_chunkIndexNorthEast * ChunkSize + Vector2.One * (ChunkSize - 1));
|
||||
HexCell cellNorthEast =
|
||||
HexGrid.GetHexAtOffset(_chunkIndexNorthEast * ChunkSize + Vector2.One * (ChunkSize - 1));
|
||||
|
||||
var centerCell =
|
||||
HexCell centerCell =
|
||||
HexGrid.GetHexAtOffset(((cellNorthEast.OffsetCoords - cellSouthWest.OffsetCoords) / 2).Round());
|
||||
var numCells = ChunkSize * Math.Max(NumChunkColumns, NumChunkRows);
|
||||
int numCells = ChunkSize * Math.Max(NumChunkColumns, NumChunkRows);
|
||||
|
||||
HexGrid.SetBoundsOffset(cellSouthWest, ChunkSize * new Vector2(NumChunkColumns, NumChunkRows));
|
||||
}
|
||||
|
||||
public override void _Process(float delta)
|
||||
{
|
||||
var oldState = State;
|
||||
GenerationState oldState = State;
|
||||
|
||||
UpdateGenerationState();
|
||||
|
||||
if (oldState != GenerationState.Done && State == GenerationState.Done)
|
||||
{
|
||||
UpdateWorldViewTexture();
|
||||
|
||||
EmitSignal("OnTilesChanged", _removedChunkIndices.ToArray(), _addedChunkIndices.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGenerationState()
|
||||
{
|
||||
FrameCounter++;
|
||||
|
||||
if (State == GenerationState.Heightmap)
|
||||
{
|
||||
var numChunksGeneratingHeightmap = 0;
|
||||
foreach (var chunkIndex in _addedChunkIndices)
|
||||
int numChunksGeneratingHeightmap = 0;
|
||||
foreach (Vector2 chunkIndex in _addedChunkIndices)
|
||||
{
|
||||
var chunk = _cachedWorldChunks[chunkIndex];
|
||||
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
|
||||
if (chunk.HeightMapFrameCount > 0) numChunksGeneratingHeightmap++;
|
||||
}
|
||||
|
||||
if (numChunksGeneratingHeightmap == 0)
|
||||
{
|
||||
// assign height map images
|
||||
foreach (var chunkIndex in _addedChunkIndices)
|
||||
foreach (Vector2 chunkIndex in _addedChunkIndices)
|
||||
{
|
||||
var chunk = _cachedWorldChunks[chunkIndex];
|
||||
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
|
||||
chunk.SetHeightmap(chunk.HeightmapOffscreenViewport.GetTexture());
|
||||
}
|
||||
|
||||
GD.Print("Switching to TileType Generation: " + FrameCounter);
|
||||
State = GenerationState.TileType;
|
||||
}
|
||||
}
|
||||
else if (State == GenerationState.TileType)
|
||||
{
|
||||
var numChunksGeneratingTileType = 0;
|
||||
foreach (var chunkIndex in _addedChunkIndices)
|
||||
int numChunksGeneratingTileType = 0;
|
||||
foreach (Vector2 chunkIndex in _addedChunkIndices)
|
||||
{
|
||||
var chunk = _cachedWorldChunks[chunkIndex];
|
||||
WorldChunk chunk = _cachedWorldChunks[chunkIndex];
|
||||
if (chunk.TileTypeMapFrameCount > 0) numChunksGeneratingTileType++;
|
||||
}
|
||||
|
||||
if (numChunksGeneratingTileType == 0) State = GenerationState.Objects;
|
||||
if (numChunksGeneratingTileType == 0)
|
||||
{
|
||||
GD.Print("Switching to Object Generation: " + FrameCounter);
|
||||
State = GenerationState.Objects;
|
||||
}
|
||||
}
|
||||
else if (State == GenerationState.Objects)
|
||||
{
|
||||
// generate objects
|
||||
foreach (var chunkIndex in _addedChunkIndices) PopulateChunk(_cachedWorldChunks[chunkIndex]);
|
||||
foreach (Vector2 chunkIndex in _addedChunkIndices)
|
||||
PopulateChunk(_cachedWorldChunks[chunkIndex]);
|
||||
|
||||
_addedChunkIndices.Clear();
|
||||
|
||||
GD.Print("Generation done: " + FrameCounter);
|
||||
State = GenerationState.Done;
|
||||
}
|
||||
}
|
||||
|
@ -508,4 +538,14 @@ public class World : Spatial
|
|||
{
|
||||
EmitSignal("EntityClicked", entity);
|
||||
}
|
||||
|
||||
public void OnTileClicked(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileClicked", tile);
|
||||
}
|
||||
|
||||
public void OnTileHovered(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileHovered", tile);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
public class WorldChunk : Spatial
|
||||
{
|
||||
private readonly PackedScene _hexTile3DScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
|
||||
private MultiMeshInstance _multiMeshInstance;
|
||||
private readonly Array<int> _tileInstanceIndices = new();
|
||||
|
||||
private readonly SpatialMaterial _rectMaterial = new();
|
||||
private Sprite _heightmapSprite;
|
||||
private TextureRect _heightmapTextureRect;
|
||||
|
@ -11,9 +16,15 @@ public class WorldChunk : Spatial
|
|||
|
||||
private Sprite _noiseSprite;
|
||||
private bool _showTextureOverlay;
|
||||
|
||||
[Export] public Vector2 ChunkIndex;
|
||||
|
||||
public Color DebugColor = Colors.White;
|
||||
[Export] public Spatial Entities;
|
||||
public Spatial Entities;
|
||||
public Spatial Tiles;
|
||||
private readonly HexGrid _hexGrid = new();
|
||||
private readonly bool _showHexTiles;
|
||||
|
||||
[Export] public Texture HeightMap;
|
||||
public int HeightMapFrameCount;
|
||||
|
||||
|
@ -23,10 +34,15 @@ public class WorldChunk : Spatial
|
|||
public bool NoiseTextureCheckerboardOverlay = true;
|
||||
|
||||
// signals
|
||||
// delegate void OnCoordClicked(Vector2 world_pos);
|
||||
[Signal]
|
||||
private delegate void TileClicked(HexTile3D tile3d);
|
||||
|
||||
[Signal]
|
||||
private delegate void TileHovered(HexTile3D tile3d);
|
||||
|
||||
// other members
|
||||
public Rect2 PlaneRect;
|
||||
|
||||
// ui elements
|
||||
|
||||
// scene nodes
|
||||
|
@ -67,7 +83,7 @@ public class WorldChunk : Spatial
|
|||
Debug.Assert(PlaneRectMesh != null);
|
||||
if (PlaneRectMesh.Visible) _showTextureOverlay = true;
|
||||
|
||||
var planeRectTransform = Transform.Identity;
|
||||
Transform planeRectTransform = Transform.Identity;
|
||||
planeRectTransform =
|
||||
planeRectTransform.Scaled(new Vector3(PlaneRect.Size.x, 0.125f, PlaneRect.Size.y));
|
||||
planeRectTransform.origin.x = PlaneRect.GetCenter().x;
|
||||
|
@ -94,6 +110,9 @@ public class WorldChunk : Spatial
|
|||
Entities = (Spatial)FindNode("Entities");
|
||||
Debug.Assert(Entities != null);
|
||||
|
||||
Tiles = (Spatial)FindNode("Tiles");
|
||||
Debug.Assert(Tiles != null);
|
||||
|
||||
SetSize(World.ChunkSize);
|
||||
}
|
||||
|
||||
|
@ -112,10 +131,98 @@ public class WorldChunk : Spatial
|
|||
}
|
||||
}
|
||||
|
||||
public void SetChunkIndex(Vector2 chunkIndex, HexGrid hexGrid)
|
||||
{
|
||||
ChunkIndex = chunkIndex;
|
||||
float chunkSize = World.ChunkSize;
|
||||
|
||||
Vector2 planeCoordSouthWest = hexGrid.GetHexCenterFromOffset(chunkIndex * chunkSize);
|
||||
|
||||
Transform = new Transform(Basis.Identity, new Vector3(planeCoordSouthWest.x, 0, planeCoordSouthWest.y));
|
||||
|
||||
Vector2 localPlaneCoordSouthWest = new Vector2(-hexGrid.HexSize.x, hexGrid.HexSize.y) * 0.5f;
|
||||
Vector2 localPlaneCoordNorthEast = hexGrid.GetHexCenterFromOffset(Vector2.One * chunkSize) +
|
||||
new Vector2(hexGrid.HexSize.x, -hexGrid.HexSize.y) * 0.5f;
|
||||
|
||||
PlaneRect = new Rect2(
|
||||
new Vector2(localPlaneCoordSouthWest.x, localPlaneCoordNorthEast.y),
|
||||
new Vector2(localPlaneCoordNorthEast.x - localPlaneCoordSouthWest.x,
|
||||
localPlaneCoordSouthWest.y - localPlaneCoordNorthEast.y)
|
||||
);
|
||||
}
|
||||
|
||||
public void InitializeTileInstances(Vector2 chunkIndex, MultiMeshInstance multiMeshInstance,
|
||||
int tileInstanceIndexStart)
|
||||
{
|
||||
_multiMeshInstance = multiMeshInstance;
|
||||
_tileInstanceIndices.Clear();
|
||||
|
||||
int chunkSize = World.ChunkSize;
|
||||
|
||||
foreach (Spatial node in Tiles.GetChildren())
|
||||
node.QueueFree();
|
||||
|
||||
foreach (int i in Enumerable.Range(0, chunkSize))
|
||||
foreach (int j in Enumerable.Range(0, chunkSize))
|
||||
{
|
||||
HexTile3D tile3D = (HexTile3D)_hexTile3DScene.Instance();
|
||||
tile3D.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||
tile3D.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||
|
||||
tile3D.Cell.OffsetCoords = new Vector2(chunkIndex * World.ChunkSize + new Vector2(i, j));
|
||||
_tileInstanceIndices.Add(tileInstanceIndexStart + _tileInstanceIndices.Count);
|
||||
|
||||
Transform tileTransform = Transform.Identity;
|
||||
Vector2 centerPlaneCoord = _hexGrid.GetHexCenterFromOffset(new Vector2(i, j));
|
||||
tileTransform.origin = new Vector3(centerPlaneCoord.x, 0, centerPlaneCoord.y);
|
||||
tile3D.Transform = tileTransform;
|
||||
|
||||
Tiles.AddChild(tile3D);
|
||||
}
|
||||
|
||||
_multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount;
|
||||
}
|
||||
|
||||
public void ClearContent()
|
||||
{
|
||||
foreach (Spatial child in Entities.GetChildren())
|
||||
child.QueueFree();
|
||||
}
|
||||
|
||||
public void UpdateTileTransforms()
|
||||
{
|
||||
Transform chunkTransform = Transform.Identity;
|
||||
Vector2 chunkOriginPlaneCoord = _hexGrid.GetHexCenterFromOffset(ChunkIndex * World.ChunkSize);
|
||||
chunkTransform.origin = new Vector3(chunkOriginPlaneCoord.x, 0, chunkOriginPlaneCoord.y);
|
||||
Transform = chunkTransform;
|
||||
|
||||
Basis tileOrientation = new(Vector3.Up, 90f * Mathf.Pi / 180f);
|
||||
|
||||
GD.Print("Updating transforms for instances of chunk " + ChunkIndex + " origin: " + chunkTransform.origin);
|
||||
|
||||
foreach (int i in Enumerable.Range(0, _tileInstanceIndices.Count))
|
||||
{
|
||||
int column = i % World.ChunkSize;
|
||||
int row = i / World.ChunkSize;
|
||||
|
||||
Vector2 tilePlaneCoord =
|
||||
_hexGrid.GetHexCenterFromOffset(new Vector2(column, row));
|
||||
|
||||
Transform hexTransform = new(tileOrientation,
|
||||
chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y));
|
||||
|
||||
if (_showHexTiles)
|
||||
hexTransform = new Transform(tileOrientation.Scaled(Vector3.One * 0.95f),
|
||||
hexTransform.origin);
|
||||
|
||||
_multiMeshInstance.Multimesh.SetInstanceTransform(_tileInstanceIndices[i], hexTransform);
|
||||
}
|
||||
}
|
||||
|
||||
// other members
|
||||
public void SaveToFile(string chunkName)
|
||||
{
|
||||
var image = new Image();
|
||||
Image image = new();
|
||||
|
||||
image.CreateFromData(Size, Size, false, Image.Format.Rgba8, TileTypeMap.GetData().GetData());
|
||||
image.SavePng(chunkName + "_tileType.png");
|
||||
|
@ -157,14 +264,14 @@ public class WorldChunk : Spatial
|
|||
|
||||
if (NoiseTextureCheckerboardOverlay)
|
||||
{
|
||||
var tileTypeImage = tileTypeTexture.GetData();
|
||||
Image tileTypeImage = tileTypeTexture.GetData();
|
||||
tileTypeImage.Lock();
|
||||
|
||||
foreach (var i in Enumerable.Range(0, Size))
|
||||
foreach (var j in Enumerable.Range(0, Size))
|
||||
foreach (int i in Enumerable.Range(0, Size))
|
||||
foreach (int j in Enumerable.Range(0, Size))
|
||||
{
|
||||
var textureCoord = new Vector2(i, j);
|
||||
var baseColor = tileTypeImage.GetPixelv(textureCoord);
|
||||
Vector2 textureCoord = new(i, j);
|
||||
Color baseColor = tileTypeImage.GetPixelv(textureCoord);
|
||||
|
||||
if ((i + j) % 2 == 0)
|
||||
tileTypeImage.SetPixelv(textureCoord, baseColor);
|
||||
|
@ -173,7 +280,7 @@ public class WorldChunk : Spatial
|
|||
}
|
||||
|
||||
tileTypeImage.Unlock();
|
||||
var imageTexture = new ImageTexture();
|
||||
ImageTexture imageTexture = new();
|
||||
imageTexture.CreateFromImage(tileTypeImage, 0);
|
||||
tileTypeTexture = imageTexture;
|
||||
}
|
||||
|
@ -198,4 +305,14 @@ public class WorldChunk : Spatial
|
|||
|
||||
PlaneRectMesh.MaterialOverride = null;
|
||||
}
|
||||
|
||||
public void OnTileClicked(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileClicked", tile);
|
||||
}
|
||||
|
||||
public void OnTileHovered(HexTile3D tile)
|
||||
{
|
||||
EmitSignal("TileHovered", tile);
|
||||
}
|
||||
}
|
|
@ -24,6 +24,8 @@ blend_mode = 3
|
|||
[node name="WorldChunk" type="Spatial"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Tiles" type="Spatial" parent="."]
|
||||
|
||||
[node name="Entities" type="Spatial" parent="."]
|
||||
|
||||
[node name="PlaneRectMesh" type="MeshInstance" parent="."]
|
||||
|
|
Loading…
Reference in New Issue