diff --git a/HexGrid.cs b/HexGrid.cs index 3700c1a..0c3f4dc 100644 --- a/HexGrid.cs +++ b/HexGrid.cs @@ -6,31 +6,33 @@ using AxialCoordDirectionPair = System.Tuple; public class HexGrid : Resource { - private Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2); - private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2); + private readonly Vector2 _baseHexSize = new(1, Mathf.Sqrt(3) / 2); + private Rect2 _boundsAxialCoords; private Vector2 _hexScale = new(1, 1); + private Vector2 _hexSize = new(1, Mathf.Sqrt(3) / 2); private Transform2D _hexTransform; private Transform2D _hexTransformInv; - private HexCell _minCoords = new(); private HexCell _maxCoords = new(); - private Rect2 _boundsAxialCoords; + private HexCell _minCoords = new(); + + public Dictionary<(Vector2, Vector3), float> Barriers = new(); + public int FindPathCheckedCellCount; public Dictionary Obstacles = new(); - public Dictionary<(Vector2, Vector3), float> Barriers = new(); - public float PathCostDefault = 1; - public int FindPathCheckedCellCount; - public Vector2 HexSize + public HexGrid() { - get { return _hexSize; } + HexScale = new Vector2(1, 1); } + public Vector2 HexSize => _hexSize; + public Vector2 HexScale { - get { return _hexScale; } + get => _hexScale; set { _hexScale = value; @@ -45,11 +47,6 @@ public class HexGrid : Resource } } - public HexGrid() - { - HexScale = new Vector2(1, 1); - } - public Vector2 GetHexCenter(HexCell cell) { return _hexTransform * cell.AxialCoords; @@ -57,21 +54,29 @@ public class HexGrid : Resource public Vector2 GetHexCenterFromOffset(Vector2 offsetCoord) { - HexCell cell = new HexCell(); + var cell = new HexCell(); cell.OffsetCoords = offsetCoord; return GetHexCenter(cell); } + public Vector3 GetHexCenterVec3FromOffset(Vector2 offsetCoord) + { + var cell = new HexCell(); + cell.OffsetCoords = offsetCoord; + var hexCenter = GetHexCenter(cell); + return new Vector3(hexCenter.x, 0, hexCenter.y); + } + public HexCell GetHexAtOffset(Vector2 offsetCoord) { - HexCell cell = new HexCell(); + var cell = new HexCell(); cell.OffsetCoords = offsetCoord; return cell; } public HexCell GetHexAt(Vector2 planeCoord) { - HexCell result = new HexCell(_hexTransformInv * planeCoord); + var result = new HexCell(_hexTransformInv * planeCoord); return result; } @@ -85,12 +90,12 @@ public class HexGrid : Resource _minCoords = minCell; _maxCoords = maxCell; _boundsAxialCoords = new Rect2(_minCoords.AxialCoords, - (_maxCoords.AxialCoords - _minCoords.AxialCoords) + Vector2.One); + _maxCoords.AxialCoords - _minCoords.AxialCoords + Vector2.One); } public void SetBounds(HexCell center, int size) { - Vector2 centerOffset = center.OffsetCoords; + var centerOffset = center.OffsetCoords; SetBounds(GetHexAtOffset(centerOffset - Vector2.One * size / 2), GetHexAtOffset(centerOffset + Vector2.One * size / 2)); } @@ -123,10 +128,7 @@ public class HexGrid : Resource public void RemoveBarrier(HexCell cell, Vector3 directionCube) { - if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) - { - Barriers.Remove((cell.AxialCoords, directionCube)); - } + if (Barriers.ContainsKey((cell.AxialCoords, directionCube))) Barriers.Remove((cell.AxialCoords, directionCube)); } public float GetHexCost(HexCell cell) @@ -136,10 +138,7 @@ public class HexGrid : Resource public float GetHexCost(Vector2 axialCoords) { - if (!_boundsAxialCoords.HasPoint(axialCoords)) - { - return 0; - } + if (!_boundsAxialCoords.HasPoint(axialCoords)) return 0; float value; return Obstacles.TryGetValue(axialCoords, out value) ? value : PathCostDefault; @@ -147,29 +146,20 @@ public class HexGrid : Resource public float GetMoveCost(Vector2 axialCoords, Vector3 directionCube) { - HexCell startCell = new HexCell(axialCoords); - HexCell targetCell = new HexCell(startCell.CubeCoords + directionCube); + var startCell = new HexCell(axialCoords); + var targetCell = new HexCell(startCell.CubeCoords + directionCube); - float cost = GetHexCost(axialCoords); - if (cost == 0) - { - return 0; - } + var cost = GetHexCost(axialCoords); + if (cost == 0) return 0; cost = GetHexCost(targetCell.AxialCoords); - if (cost == 0) - { - return 0; - } + if (cost == 0) return 0; float barrierCost; if (Barriers.ContainsKey((axialCoords, directionCube))) { barrierCost = Barriers[(axialCoords, directionCube)]; - if (barrierCost == 0) - { - return 0; - } + if (barrierCost == 0) return 0; cost += barrierCost; } @@ -177,10 +167,7 @@ public class HexGrid : Resource if (Barriers.ContainsKey((targetCell.AxialCoords, -directionCube))) { barrierCost = Barriers[(targetCell.AxialCoords, -directionCube)]; - if (barrierCost == 0) - { - return 0; - } + if (barrierCost == 0) return 0; cost += barrierCost; } @@ -193,16 +180,14 @@ public class HexGrid : Resource { if (GetHexCost(toCell) == 0) { - HexCell[] line = fromCell.LineTo(toCell); + var line = fromCell.LineTo(toCell); - foreach (int i in Enumerable.Range(1, line.Length)) - { + foreach (var i in Enumerable.Range(1, line.Length)) if (GetHexCost(line[i]) == 0) { toCell = line[i - 1]; break; } - } } return toCell; @@ -210,13 +195,13 @@ public class HexGrid : Resource public List FindPath(HexCell startHex, HexCell goalHex) { - Vector2 goalAxialCoords = goalHex.AxialCoords; + var goalAxialCoords = goalHex.AxialCoords; - SimplePriorityQueue frontier = new SimplePriorityQueue(); + var frontier = new SimplePriorityQueue(); frontier.Enqueue(startHex.AxialCoords, 0); - Dictionary cameFrom = + var cameFrom = new Dictionary(); - Dictionary costSoFar = + var costSoFar = new Dictionary(); cameFrom.Add(startHex.AxialCoords, startHex.AxialCoords); @@ -227,20 +212,17 @@ public class HexGrid : Resource while (frontier.Any()) { FindPathCheckedCellCount++; - HexCell currentHex = new HexCell(frontier.Dequeue()); - Vector2 currentAxial = currentHex.AxialCoords; + var currentHex = new HexCell(frontier.Dequeue()); + var currentAxial = currentHex.AxialCoords; - if (currentHex == goalHex) + if (currentHex == goalHex) break; + + foreach (var nextHex in currentHex.GetAllAdjacent()) { - break; - } + var nextAxial = nextHex.AxialCoords; + var nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords); - foreach (HexCell nextHex in currentHex.GetAllAdjacent()) - { - Vector2 nextAxial = nextHex.AxialCoords; - float nextCost = GetMoveCost(currentAxial, new HexCell(nextAxial - currentHex.AxialCoords).CubeCoords); - - if ((nextHex == goalHex) && (GetHexCost(nextAxial) == 0)) + if (nextHex == goalHex && GetHexCost(nextAxial) == 0) { // Goal ist an obstacle cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; @@ -248,16 +230,13 @@ public class HexGrid : Resource break; } - if (nextCost == 0) - { - continue; - } + if (nextCost == 0) continue; nextCost += costSoFar[currentHex.AxialCoords]; if (!costSoFar.ContainsKey(nextHex.AxialCoords) || nextCost < costSoFar[nextHex.AxialCoords]) { costSoFar[nextHex.AxialCoords] = nextCost; - float priority = nextCost + nextHex.DistanceTo(goalHex); + var priority = nextCost + nextHex.DistanceTo(goalHex); frontier.Enqueue(nextHex.AxialCoords, priority); cameFrom[nextHex.AxialCoords] = currentHex.AxialCoords; @@ -267,19 +246,16 @@ public class HexGrid : Resource // GD.Print("Checked Cell Count: " + FindPathCheckedCellCount); - List result = new List(); + var result = new List(); if (!cameFrom.ContainsKey(goalHex.AxialCoords)) { GD.Print("Failed to find path from " + startHex + " to " + goalHex); return result; } - if (GetHexCost(goalAxialCoords) != 0) - { - result.Add(goalHex); - } + if (GetHexCost(goalAxialCoords) != 0) result.Add(goalHex); - HexCell pathHex = goalHex; + var pathHex = goalHex; while (pathHex != startHex) { pathHex = new HexCell(cameFrom[pathHex.AxialCoords]); @@ -292,26 +268,26 @@ public class HexGrid : Resource public List GetCellsForLine(Vector2 fromPlane, Vector2 toPlane) { - List result = new List(); + var result = new List(); - float distance = (toPlane - fromPlane).Length(); - Vector2 direction = (toPlane - fromPlane) / distance; + var distance = (toPlane - fromPlane).Length(); + var direction = (toPlane - fromPlane) / distance; - Vector2 currentPointPlane = fromPlane; - HexCell currentCell = GetHexAt(currentPointPlane); + var currentPointPlane = fromPlane; + var currentCell = GetHexAt(currentPointPlane); float currentDistance = 0; do { result.Add(currentCell); GetHexCenter(currentCell); - Vector2 currentPointLocal = currentPointPlane - GetHexCenter(currentCell); + var currentPointLocal = currentPointPlane - GetHexCenter(currentCell); int neighbourIndex; float boundaryPlaneDistance; currentCell.QueryClosestCellBoundary(currentPointLocal, direction, out neighbourIndex, out boundaryPlaneDistance); - + currentCell = currentCell.GetAdjacent(HexCell.NeighborDirections[neighbourIndex]); currentDistance += boundaryPlaneDistance * 1.001f; currentPointPlane = fromPlane + direction * boundaryPlaneDistance; diff --git a/export_presets.cfg b/export_presets.cfg index f46cbda..9ca3e63 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -2,7 +2,7 @@ name="PirateGame3D" platform="Android" -runnable=true +runnable=false custom_features="" export_filter="all_resources" include_filter="" @@ -15,7 +15,7 @@ script_encryption_key="" custom_template/debug="" custom_template/release="" -custom_build/use_custom_build=true +custom_build/use_custom_build=false custom_build/export_format=0 custom_build/min_sdk="" custom_build/target_sdk="" diff --git a/project.godot b/project.godot index f9b6e9d..579d6ec 100644 --- a/project.godot +++ b/project.godot @@ -132,5 +132,7 @@ common/enable_pause_aware_picking=true [rendering] -quality/shadows/filter_mode.mobile=1 +quality/directional_shadow/size.mobile=512 +quality/shadow_atlas/size.mobile=1024 +quality/subsurface_scattering/quality=0 environment/default_environment="res://default_env.tres" diff --git a/scenes/Game.tscn b/scenes/Game.tscn index 2c54e12..94d1e84 100644 --- a/scenes/Game.tscn +++ b/scenes/Game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=19 format=2] +[gd_scene load_steps=24 format=2] [ext_resource path="res://scenes/StreamContainer.tscn" type="PackedScene" id=1] [ext_resource path="res://entities/Player.tscn" type="PackedScene" id=2] @@ -15,6 +15,11 @@ [ext_resource path="res://assets/Environment/HexTileMesh.tres" type="CylinderMesh" id=13] [ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14] [ext_resource path="res://systems/InteractionSystem.cs" type="Script" id=15] +[ext_resource path="res://entities/rockA.tscn" type="PackedScene" id=16] +[ext_resource path="res://entities/rockC.tscn" type="PackedScene" id=17] +[ext_resource path="res://entities/rockB.tscn" type="PackedScene" id=18] +[ext_resource path="res://entities/Tree.tscn" type="PackedScene" id=19] +[ext_resource path="res://assets/Environment/grassLarge.tscn" type="PackedScene" id=20] [sub_resource type="Animation" id=25] resource_name = "FlashLabel" @@ -35,7 +40,9 @@ tracks/0/keys = { [sub_resource type="AnimationNodeStateMachinePlayback" id=26] [sub_resource type="MultiMesh" id=27] +color_format = 1 transform_format = 1 +custom_data_format = 1 visible_instance_count = 0 mesh = ExtResource( 13 ) @@ -351,9 +358,6 @@ script = ExtResource( 15 ) [node name="Player" parent="." instance=ExtResource( 2 )] TileWorldNode = NodePath("../TileWorld") -[node name="Skeleton" parent="Player/Geometry/Armature" index="0"] -bones/4/bound_children = [ ] - [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 ) @@ -382,6 +386,26 @@ World = NodePath("..") transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.5, 0 ) multimesh = SubResource( 27 ) +[node name="Assets" type="Spatial" parent="World"] +transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -5, 0 ) +visible = false + +[node name="Rocks" type="Spatial" parent="World/Assets"] + +[node name="rockA" parent="World/Assets/Rocks" instance=ExtResource( 16 )] + +[node name="rockB" parent="World/Assets/Rocks" instance=ExtResource( 18 )] + +[node name="rockC" parent="World/Assets/Rocks" instance=ExtResource( 17 )] + +[node name="Grass" type="Spatial" parent="World/Assets"] + +[node name="grassLarge" parent="World/Assets/Grass" instance=ExtResource( 20 )] + +[node name="Trees" type="Spatial" parent="World/Assets"] + +[node name="tree" parent="World/Assets/Trees" instance=ExtResource( 19 )] + [connection signal="toggled" from="DebugContainer/DebugStatsContainer/DebugMenuButton" to="DebugContainer/DebugStatsContainer" method="_on_DebugMenuButton_toggled"] [connection signal="value_changed" from="Generator Container/WorldGeneratorContainer/HBoxContainer/WorldSizeSlider" to="Generator Container/WorldGeneratorContainer" method="_on_HSlider_value_changed"] [connection signal="toggled" from="Generator Container/WorldGeneratorContainer/ShowTexturesCheckButton" to="Generator Container/WorldGeneratorContainer" method="_on_ShowTexturesCheckButton_toggled"] diff --git a/scenes/TileInstanceManager.cs b/scenes/TileInstanceManager.cs index 056fcb6..e234cb0 100644 --- a/scenes/TileInstanceManager.cs +++ b/scenes/TileInstanceManager.cs @@ -142,6 +142,7 @@ public class TileInstanceManager : Spatial 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)); @@ -163,7 +164,7 @@ public class TileInstanceManager : Spatial _multiMeshInstance.Multimesh.VisibleInstanceCount = _multiMeshInstance.Multimesh.InstanceCount; GD.Print("VisibleInstanceCount = " + _multiMeshInstance.Multimesh.VisibleInstanceCount); - + ChunkIndex = chunkIndex; } @@ -181,8 +182,8 @@ public class TileInstanceManager : Spatial var tileOrientation = new Basis(Vector3.Up, 90f * Mathf.Pi / 180f); - GD.Print("Updating transforms for instances of chunk " + value); - + 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; @@ -190,6 +191,7 @@ public class TileInstanceManager : Spatial var tilePlaneCoord = HexGrid.GetHexCenterFromOffset(new Vector2(column, row)); + _multiMeshInstance.Multimesh.SetInstanceTransform(TileInstanceIndices[i], new Transform(tileOrientation, chunkTransform.origin + new Vector3(tilePlaneCoord.x, 0, tilePlaneCoord.y))); diff --git a/scenes/World.cs b/scenes/World.cs index 6fab432..427ae55 100644 --- a/scenes/World.cs +++ b/scenes/World.cs @@ -17,16 +17,18 @@ public class World : Spatial } // constants - public const int ChunkSize = 16; + public const int ChunkSize = 10; public const int NumChunkRows = 3; public const int NumChunkColumns = NumChunkRows; + private static readonly Color RockColor = new(0.5f, 0.5f, 0.4f); + private static readonly Color GrassColor = new(0, 0.4f, 0); + 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 _addedChunkIndices = new(); private readonly Godot.Collections.Dictionary _cachedWorldChunks; - private readonly Image _heightmapImage = new(); - private readonly List _removedChunkIndices = new(); - private readonly Image _tileTypeMapImage = new(); // referenced scenes @@ -39,10 +41,13 @@ public class World : Spatial // other members private Vector2 _centerPlaneCoord; + private Array _grassAssets; private ImageTexture _heightmapTexture; private OpenSimplexNoise _noiseGenerator = new(); + private Array _rockAssets; private TileInstanceManager _tileInstanceManager; + private Array _treeAssets; private ImageTexture _viewTileTypeTexture; public Vector2 CenterChunkIndex = Vector2.Zero; public Spatial Chunks; @@ -71,6 +76,17 @@ public class World : Spatial InitNoiseGenerator(); + GetNode("Assets").Visible = false; + + _rockAssets = new Array(); + foreach (Spatial asset in GetNode("Assets/Rocks").GetChildren()) _rockAssets.Add(asset); + + _grassAssets = new Array(); + foreach (Spatial asset in GetNode("Assets/Grass").GetChildren()) _grassAssets.Add(asset); + + _treeAssets = new Array(); + foreach (Spatial asset in GetNode("Assets/Trees").GetChildren()) _treeAssets.Add(asset); + SetCenterPlaneCoord(Vector2.Zero); } @@ -129,6 +145,78 @@ public class World : Spatial 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); + return colorDifference.LengthSquared() < 0.1 * 0.1; + } + + private Spatial SelectAsset(Vector2 offsetCoord, Array 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); + // TODO: assetTransform.origin.y = GetHeightAtOffset(offsetCoord); + assetTransform.origin.y = 0; + assetTransform.basis = + assetTransform.basis.Rotated(Vector3.Up, (float)(randomGenerator.NextDouble() * Mathf.Pi * 2)); + assetInstance.Transform = assetTransform; + + return assetInstance; + } + + private void PopulateChunk(WorldChunk chunk) + { + var environmentRandom = new Random(Seed); + + var 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)) + { + var colorValue = tileTypeImage.GetPixel(textureCoordU, textureCoordV); + var textureCoord = new Vector2(textureCoordU, textureCoordV); + var offsetCoord = chunk.ChunkIndex * ChunkSize + textureCoord; + + if (IsColorEqualApprox(colorValue, RockColor)) + { + var rockAsset = SelectAsset(offsetCoord, _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); + if (grassAsset != null) chunk.Entities.AddChild(grassAsset); + + var treeAsset = SelectAsset(offsetCoord, _treeAssets, environmentRandom, 0.05); + if (treeAsset != null) chunk.Entities.AddChild(treeAsset); + // TODO: MarkCellUnwalkable(cell); + // else if (environmentRandom.NextDouble() < 0.01) +// { +// var chestAsset = (Chest)_chestScene.Instance(); +// var assetTransform = Transform.Identity; +// assetTransform.origin = GetHexCenterFromOffset(offsetCoord); +// assetTransform.origin.y = GetHeightAtOffset(offsetCoord); +// chestAsset.Transform = assetTransform; +// Entities.AddChild(chestAsset); +// MarkCellUnwalkable(cell); +// } + } +// else if (IsColorWater(colorValue)) +// { +// MarkCellUnwalkable(cell); +// } + } + + tileTypeImage.Unlock(); + } + public void UpdateCenterChunkFromPlaneCoord(Vector2 planeCoord) { if (State != GenerationState.Done) @@ -338,6 +426,8 @@ public class World : Spatial else if (State == GenerationState.Objects) { // generate objects + foreach (var chunkIndex in _addedChunkIndices) PopulateChunk(_cachedWorldChunks[chunkIndex]); + State = GenerationState.Done; } } diff --git a/scenes/WorldChunk.cs b/scenes/WorldChunk.cs index 1e1f990..b3a5b8f 100644 --- a/scenes/WorldChunk.cs +++ b/scenes/WorldChunk.cs @@ -4,16 +4,16 @@ using Godot; public class WorldChunk : Spatial { + private readonly SpatialMaterial _rectMaterial = new(); private Sprite _heightmapSprite; private TextureRect _heightmapTextureRect; private Sprite _noiseMask; private Sprite _noiseSprite; - - private readonly SpatialMaterial _rectMaterial = new(); private bool _showTextureOverlay; [Export] public Vector2 ChunkIndex; public Color DebugColor = Colors.White; + [Export] public Spatial Entities; [Export] public Texture HeightMap; public int HeightMapFrameCount; @@ -91,6 +91,9 @@ public class WorldChunk : Spatial TileTypeOffscreenViewport.Size = Vector2.One * Size; Debug.Assert(TileTypeOffscreenViewport != null); + Entities = (Spatial)FindNode("Entities"); + Debug.Assert(Entities != null); + SetSize(World.ChunkSize); }