Noise based map generation, movement component uses height.

WorldChunkRefactoring
Martin Felis 2022-12-28 16:22:53 +01:00
parent 12c1e4df86
commit 041d580550
17 changed files with 401 additions and 166 deletions

View File

@ -6,6 +6,8 @@
<!-- See https://www.reddit.com/r/godot/comments/l0naqg/comment/gjwfspm/ --> <!-- See https://www.reddit.com/r/godot/comments/l0naqg/comment/gjwfspm/ -->
<!-- And https://github.com/godotengine/godot/issues/42271#issuecomment-751423827 --> <!-- And https://github.com/godotengine/godot/issues/42271#issuecomment-751423827 -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<DefaultItemExcludes>$(DefaultItemExcludes);script_templates/*</DefaultItemExcludes>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -16,7 +16,10 @@ public class HexCell : Resource
public static readonly Array DIR_ALL = new Array() public static readonly Array DIR_ALL = new Array()
{ DIR_N, DIR_NE, DIR_SE, DIR_S, DIR_SW, DIR_NW }; { DIR_N, DIR_NE, DIR_SE, DIR_S, DIR_SW, DIR_NW };
public HexCell() { } public HexCell()
{
}
public HexCell(float cubeX, float cubeY, float cubeZ) public HexCell(float cubeX, float cubeY, float cubeZ)
{ {
CubeCoords = RoundCoords(new Vector3(cubeX, cubeY, cubeZ)); CubeCoords = RoundCoords(new Vector3(cubeX, cubeY, cubeZ));
@ -43,18 +46,17 @@ public class HexCell : Resource
} }
private Vector3 _cubeCoords; private Vector3 _cubeCoords;
public Vector3 CubeCoords public Vector3 CubeCoords
{ {
get get { return _cubeCoords; }
{
return _cubeCoords;
}
set set
{ {
if (Mathf.Abs(value.x + value.y + value.z) > 0.0001) if (Mathf.Abs(value.x + value.y + value.z) > 0.0001)
{ {
GD.Print("Warning: Invalid cube coordinates for hex (x + y + z != 0): ", value.ToString()); GD.Print("Warning: Invalid cube coordinates for hex (x + y + z != 0): ", value.ToString());
} }
_cubeCoords = RoundCoords(value); _cubeCoords = RoundCoords(value);
} }
} }
@ -63,10 +65,7 @@ public class HexCell : Resource
{ {
get => new Vector2(CubeCoords.x, CubeCoords.y); get => new Vector2(CubeCoords.x, CubeCoords.y);
set set { CubeCoords = AxialToCubeCoords(value); }
{
CubeCoords = AxialToCubeCoords(value);
}
} }
public Vector2 OffsetCoords public Vector2 OffsetCoords

View File

@ -13,18 +13,16 @@ public class HexGrid : Resource
{ {
get { return _hexSize; } get { return _hexSize; }
} }
public Vector2 HexScale public Vector2 HexScale
{ {
get get { return _hexScale; }
{
return _hexScale;
}
set set
{ {
_hexScale = value; _hexScale = value;
_hexSize = _baseHexSize * _hexScale; _hexSize = _baseHexSize * _hexScale;
_hexTransform = new Transform2D( _hexTransform = new Transform2D(
new Vector2(_hexSize.x * 3/4, -_hexSize.y / 2), new Vector2(_hexSize.x * 3 / 4, -_hexSize.y / 2),
new Vector2(0, -_hexSize.y), new Vector2(0, -_hexSize.y),
new Vector2(0, 0) new Vector2(0, 0)
); );
@ -62,5 +60,4 @@ public class HexGrid : Resource
HexCell result = new HexCell(_hexTransformInv * planeCoord); HexCell result = new HexCell(_hexTransformInv * planeCoord);
return result; return result;
} }
} }

View File

@ -42,7 +42,6 @@ public class MovableComponent : Node
if (currentAngle < Mathf.Pi && targetAngle > Mathf.Pi) if (currentAngle < Mathf.Pi && targetAngle > Mathf.Pi)
{ {
} }
if (targetAngle - currentAngle > Mathf.Pi) if (targetAngle - currentAngle > Mathf.Pi)
@ -57,7 +56,8 @@ public class MovableComponent : Node
if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01) if (Mathf.Abs(currentAngularVelocity) > 0.1 || Mathf.Abs(targetAngle - currentAngle) > 0.01)
{ {
var springDamperResult = _angleSpringDamper.Calc(currentAngle, currentAngularVelocity, targetAngle, delta); var springDamperResult =
_angleSpringDamper.Calc(currentAngle, currentAngularVelocity, targetAngle, delta);
currentAngle = springDamperResult.Item1; currentAngle = springDamperResult.Item1;
currentAngularVelocity = springDamperResult.Item2; currentAngularVelocity = springDamperResult.Item2;
@ -65,11 +65,12 @@ public class MovableComponent : Node
EmitSignal("OrientationUpdated", this.currentAngle); EmitSignal("OrientationUpdated", this.currentAngle);
} }
if (( Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3) if ((Mathf.Abs(currentAngularVelocity) < 5 && Mathf.Abs(targetAngle - currentAngle) < 0.3)
&& (targetPosition - currentPosition).LengthSquared() > 0.01) && (targetPosition - currentPosition).LengthSquared() > 0.01)
{ {
var springDamperResult = var springDamperResult =
_positionSpringDamper.CalcClampedSpeed(currentPosition, currentVelocity, targetPosition, delta, maxSpeed); _positionSpringDamper.CalcClampedSpeed(currentPosition, currentVelocity, targetPosition, delta,
maxSpeed);
currentPosition = springDamperResult.Item1; currentPosition = springDamperResult.Item1;
currentVelocity = springDamperResult.Item2; currentVelocity = springDamperResult.Item2;

View File

@ -0,0 +1,21 @@
using Godot;
using System;
public class WorldInfoComponent : Node
{
[Export] public NodePath World;
private HexTile3D _currentHexTile3D;
public TileWorld TileWorld;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
TileWorld = GetNode<TileWorld>(World);
}
public void SetWorld(TileWorld tileWorld)
{
TileWorld = tileWorld;
}
}

View File

@ -3,8 +3,11 @@ using System;
public class Player : KinematicBody public class Player : KinematicBody
{ {
// other members
private MovableComponent _movable; private MovableComponent _movable;
private Spatial _geometry; private Spatial _geometry;
private WorldInfoComponent _worldInfo;
private Vector2 _offsetCoord = Vector2.Zero;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
@ -17,10 +20,29 @@ public class Player : KinematicBody
} }
_geometry = (Spatial)FindNode("Geometry", false); _geometry = (Spatial)FindNode("Geometry", false);
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
} }
private void OnPositionUpdated(Vector3 newPosition) public void OnPositionUpdated(Vector3 newPosition)
{ {
if (_worldInfo != null)
{
Vector2 new_offset_coord = _worldInfo.TileWorld.WorldToOffsetCoords(newPosition);
if (_offsetCoord != new_offset_coord)
{
GD.Print("New offset coord " + new_offset_coord);
_offsetCoord = new_offset_coord;
}
if (_movable != null)
{
_movable.targetPosition.y = _worldInfo.TileWorld.GetHeightAtOffset(new_offset_coord);
}
}
Transform transform = Transform; Transform transform = Transform;
transform.origin = newPosition; transform.origin = newPosition;
Transform = transform; Transform = transform;
@ -28,6 +50,6 @@ public class Player : KinematicBody
private void OnOrientationUpdated(float newOrientation) private void OnOrientationUpdated(float newOrientation)
{ {
_geometry.Transform = new Transform (new Quat(Vector3.Up, newOrientation), Vector3.Zero); _geometry.Transform = new Transform(new Quat(Vector3.Up, newOrientation), Vector3.Zero);
} }
} }

View File

@ -0,0 +1,4 @@
[gd_resource type="SpatialMaterial" format=2]
[resource]
albedo_color = Color( 1, 0, 1, 1 )

View File

@ -9,17 +9,17 @@
config_version=4 config_version=4
_global_script_classes=[ { _global_script_classes=[ {
"base": "Node", "base": "Reference",
"class": "ClickableComponent", "class": "ClickableComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/ClickableComponent.gd" "path": "res://components/ClickableComponent.gd"
}, { }, {
"base": "KinematicBody2D", "base": "Reference",
"class": "CollisionLine", "class": "CollisionLine",
"language": "GDScript", "language": "GDScript",
"path": "res://utils/CollisionLine.gd" "path": "res://utils/CollisionLine.gd"
}, { }, {
"base": "Node", "base": "Reference",
"class": "ColorComponent", "class": "ColorComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/ColorComponent.gd" "path": "res://components/ColorComponent.gd"
@ -54,7 +54,7 @@ _global_script_classes=[ {
"language": "GDScript", "language": "GDScript",
"path": "res://utils/SpringDamper.gd" "path": "res://utils/SpringDamper.gd"
}, { }, {
"base": "Sprite", "base": "Reference",
"class": "TintedSpriteComponent", "class": "TintedSpriteComponent",
"language": "GDScript", "language": "GDScript",
"path": "res://components/TintedSpriteComponent.gd" "path": "res://components/TintedSpriteComponent.gd"

View File

@ -1,6 +1,8 @@
using Godot; using Godot;
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using GoDotLog;
using Dictionary = Godot.Collections.Dictionary; using Dictionary = Godot.Collections.Dictionary;
using Array = Godot.Collections.Array; using Array = Godot.Collections.Array;
@ -15,6 +17,7 @@ public class AdaptiveWorldStream : Spatial
private Label _mouseTileLabel; private Label _mouseTileLabel;
private Label _numCoordsAddedLabel; private Label _numCoordsAddedLabel;
private Label _numCoordsRemovedLabel; private Label _numCoordsRemovedLabel;
private TextureRect _worldTextureRect;
// scene nodes // scene nodes
private Spatial _tileHighlight; private Spatial _tileHighlight;
@ -23,6 +26,7 @@ public class AdaptiveWorldStream : Spatial
private Area _streamContainerArea; private Area _streamContainerArea;
private Spatial _streamContainerActiveTiles; private Spatial _streamContainerActiveTiles;
private Player _player; private Player _player;
private TileWorld _tileWorld;
// Resources // Resources
private PackedScene _tileHighlightScene; private PackedScene _tileHighlightScene;
@ -45,6 +49,7 @@ public class AdaptiveWorldStream : Spatial
_mouseTileLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/mouse_tile_label"); _mouseTileLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/mouse_tile_label");
_numCoordsAddedLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/num_coords_added_label"); _numCoordsAddedLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/num_coords_added_label");
_numCoordsRemovedLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/num_coords_removed_label"); _numCoordsRemovedLabel = GetNode<Label>("Control/HBoxContainer/GridContainer/num_coords_removed_label");
_worldTextureRect = GetNode<TextureRect>("Control/WorldTextureRect");
// scene nodes // scene nodes
_tileHighlight = GetNode<Spatial>("TileHighlight"); _tileHighlight = GetNode<Spatial>("TileHighlight");
@ -53,6 +58,9 @@ public class AdaptiveWorldStream : Spatial
_streamContainerArea = GetNode<Area>("StreamContainer/Area"); _streamContainerArea = GetNode<Area>("StreamContainer/Area");
_streamContainerActiveTiles = GetNode<Spatial>("StreamContainer/ActiveTiles"); _streamContainerActiveTiles = GetNode<Spatial>("StreamContainer/ActiveTiles");
_player = GetNode<Player>("Player"); _player = GetNode<Player>("Player");
_tileWorld = GetNode<TileWorld>("TileWorld");
Debug.Assert(_tileWorld != null);
// resources // resources
_tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn"); _tileHighlightScene = GD.Load<PackedScene>("utils/TileHighlight.tscn");
@ -63,10 +71,23 @@ public class AdaptiveWorldStream : Spatial
_currentTileOffset = new Vector2(); _currentTileOffset = new Vector2();
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
// update data
_worldTextureRect.RectSize = _tileWorld.Size;
// connect signals // connect signals
_streamContainerArea.Connect("input_event", this, nameof(OnAreaInputEvent)); _streamContainerArea.Connect("input_event", this, nameof(OnAreaInputEvent));
_streamContainer.Connect("TileSelected", this, nameof(OnTileSelected)); _streamContainer.Connect("TileSelected", this, nameof(OnTileSelected));
_tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated));
// perform dependency injection
//_streamContainer.SetWorld(_tileWorld);
WorldInfoComponent worldInfoComponent = _player.GetNode<WorldInfoComponent>("WorldInfo");
if (worldInfoComponent != null)
{
worldInfoComponent.SetWorld(_tileWorld);
}
_tileWorld.Generate();
UpdateCurrentTile(); UpdateCurrentTile();
_streamContainer.SetCenterTile(_currentTile); _streamContainer.SetCenterTile(_currentTile);
} }
@ -101,10 +122,10 @@ public class AdaptiveWorldStream : Spatial
_tileLabel.Text = playerTransform.ToString(); _tileLabel.Text = playerTransform.ToString();
_tileOffsetLabel.Text = _currentTile.OffsetCoords.ToString(); _tileOffsetLabel.Text = _currentTile.OffsetCoords.ToString();
//playerTransform.origin += new Vector3(-0.1f, 0, 1) * delta;
_player.Transform = playerTransform; _player.Transform = playerTransform;
} }
public override void _Process(float delta) public override void _Process(float delta)
{ {
_framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString(); _framesPerSecondLabel.Text = Engine.GetFramesPerSecond().ToString();
@ -121,7 +142,6 @@ public class AdaptiveWorldStream : Spatial
if (_currentTile.CubeCoords != _lastTile.CubeCoords) if (_currentTile.CubeCoords != _lastTile.CubeCoords)
{ {
_streamContainer.SetCenterTile(_currentTile); _streamContainer.SetCenterTile(_currentTile);
// CreateStreamActiveTiles();
_numTilesLabel.Text = _streamContainerActiveTiles.GetChildCount().ToString(); _numTilesLabel.Text = _streamContainerActiveTiles.GetChildCount().ToString();
_numCoordsAddedLabel.Text = _streamContainer.AddedCoords.Count.ToString(); _numCoordsAddedLabel.Text = _streamContainer.AddedCoords.Count.ToString();
@ -129,7 +149,9 @@ public class AdaptiveWorldStream : Spatial
} }
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, int shapeIndex)
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex)
{ {
HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z)); HexCell cellAtCursor = _hexGrid.GetHexAt(new Vector2(position.x, position.z));
Transform highlightTransform = Transform.Identity; Transform highlightTransform = Transform.Identity;
@ -140,15 +162,15 @@ public class AdaptiveWorldStream : Spatial
_mouseWorldLabel.Text = position.ToString(); _mouseWorldLabel.Text = position.ToString();
_mouseTileLabel.Text = cellAtCursor.OffsetCoords.ToString(); _mouseTileLabel.Text = cellAtCursor.OffsetCoords.ToString();
_mouseTileHighlight.Transform = highlightTransform; _mouseTileHighlight.Transform = highlightTransform;
if (inputEvent is InputEventMouseButton) if (inputEvent is InputEventMouseButton && ((InputEventMouseButton) inputEvent).Pressed)
{ {
_player.Transform = highlightTransform; _streamContainer.EmitSignal("TileSelected", _streamContainer.GetTile3dAt(cellAtCursor.OffsetCoords));
} }
} }
public void OnTileSelected(HexTile3D tile) public void OnTileSelected(HexTile3D tile)
{ {
if (_player == null) if (_player == null)
@ -164,4 +186,17 @@ public class AdaptiveWorldStream : Spatial
movableComponent.targetPosition = tile.Transform.origin; movableComponent.targetPosition = tile.Transform.origin;
} }
public void OnWorldGenerated()
{
GD.Print("Using new map");
ImageTexture new_world_texture = new ImageTexture();
_tileWorld.Heightmap.Unlock();
new_world_texture.CreateFromImage(_tileWorld.Heightmap);
_tileWorld.Heightmap.Lock();
_worldTextureRect.Texture = new_world_texture;
}
} }

View File

@ -1,13 +1,14 @@
[gd_scene load_steps=14 format=2] [gd_scene load_steps=15 format=2]
[ext_resource path="res://scenes/AdaptiveWorldStream.cs" type="Script" id=1] [ext_resource path="res://scenes/AdaptiveWorldStream.cs" type="Script" id=1]
[ext_resource path="res://assets/CreatusPiratePack/characters/Pirate1final_0.01.glb" type="PackedScene" id=2] [ext_resource path="res://assets/CreatusPiratePack/characters/Pirate1final_0.01.glb" type="PackedScene" id=2]
[ext_resource path="res://scenes/World.gd" type="Script" id=3] [ext_resource path="res://components/WorldInfoComponent.cs" type="Script" id=3]
[ext_resource path="res://scenes/StreamContainer.cs" type="Script" id=4] [ext_resource path="res://scenes/StreamContainer.cs" type="Script" id=4]
[ext_resource path="res://entities/Player.cs" type="Script" id=5] [ext_resource path="res://entities/Player.cs" type="Script" id=5]
[ext_resource path="res://scenes/DebugCamera.gd" type="Script" id=6] [ext_resource path="res://scenes/DebugCamera.gd" type="Script" id=6]
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=7] [ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=7]
[ext_resource path="res://components/MovableComponent.cs" type="Script" id=8] [ext_resource path="res://components/MovableComponent.cs" type="Script" id=8]
[ext_resource path="res://scenes/TileWorld.cs" type="Script" id=9]
[sub_resource type="CubeMesh" id=1] [sub_resource type="CubeMesh" id=1]
size = Vector3( 1, 1, 1 ) size = Vector3( 1, 1, 1 )
@ -32,13 +33,14 @@ radial_segments = 16
script = ExtResource( 1 ) script = ExtResource( 1 )
[node name="TileHighlight" parent="." instance=ExtResource( 7 )] [node name="TileHighlight" parent="." instance=ExtResource( 7 )]
visible = false
[node name="MouseTileHighlight" parent="." instance=ExtResource( 7 )] [node name="MouseTileHighlight" parent="." instance=ExtResource( 7 )]
[node name="World" type="Spatial" parent="."] [node name="TileWorld" type="Spatial" parent="."]
script = ExtResource( 3 ) script = ExtResource( 9 )
[node name="DirectionalLight" type="DirectionalLight" parent="World"] [node name="DirectionalLight" type="DirectionalLight" parent="TileWorld"]
transform = Transform( 0.328059, -0.878387, 0.347583, 0, 0.367946, 0.929847, -0.944657, -0.305045, 0.120708, 0, 6.59293, 1.20265 ) transform = Transform( 0.328059, -0.878387, 0.347583, 0, 0.367946, 0.929847, -0.944657, -0.305045, 0.120708, 0, 6.59293, 1.20265 )
shadow_enabled = true shadow_enabled = true
directional_shadow_mode = 0 directional_shadow_mode = 0
@ -178,9 +180,16 @@ margin_right = 127.0
margin_bottom = 158.0 margin_bottom = 158.0
text = "0" text = "0"
[node name="WorldTextureRect" type="TextureRect" parent="Control"]
margin_left = 1.0
margin_top = 300.0
margin_right = 101.0
margin_bottom = 400.0
[node name="StreamContainer" type="Spatial" parent="."] [node name="StreamContainer" type="Spatial" parent="."]
script = ExtResource( 4 ) script = ExtResource( 4 )
Dimensions = Vector2( 18, 17 ) Dimensions = Vector2( 18, 17 )
World = NodePath("../TileWorld")
[node name="ActiveTiles" type="Spatial" parent="StreamContainer"] [node name="ActiveTiles" type="Spatial" parent="StreamContainer"]
@ -222,3 +231,7 @@ script = ExtResource( 8 )
[node name="Pirate1final_001" parent="Player/Geometry" instance=ExtResource( 2 )] [node name="Pirate1final_001" parent="Player/Geometry" instance=ExtResource( 2 )]
transform = Transform( 0.4, 0, 0, 0, 0.4, 0, 0, 0, 0.4, 0, 0, 0 ) transform = Transform( 0.4, 0, 0, 0, 0.4, 0, 0, 0, 0.4, 0, 0, 0 )
[node name="WorldInfo" type="Node" parent="Player"]
script = ExtResource( 3 )
World = NodePath("../../TileWorld")

View File

@ -4,10 +4,12 @@ public class HexTile3D : Spatial
{ {
public enum TileType public enum TileType
{ {
Undefined,
Sand, Sand,
Grass, Grass,
DeepGrass DeepGrass
} }
static public TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass }; static public TileType[] ValidTileTypes = { TileType.Sand, TileType.Grass, TileType.DeepGrass };
// scene nodes // scene nodes
@ -19,6 +21,7 @@ public class HexTile3D : Spatial
delegate void TileSelected(HexTile3D tile3d); delegate void TileSelected(HexTile3D tile3d);
// other member variables // other member variables
private SpatialMaterial _undefinedMaterial;
private SpatialMaterial _sandMaterial; private SpatialMaterial _sandMaterial;
private SpatialMaterial _grassMaterial; private SpatialMaterial _grassMaterial;
private SpatialMaterial _deepGrassMaterial; private SpatialMaterial _deepGrassMaterial;
@ -31,10 +34,7 @@ public class HexTile3D : Spatial
public Vector2 OffsetCoords public Vector2 OffsetCoords
{ {
get get { return Cell.OffsetCoords; }
{
return Cell.OffsetCoords;
}
set set
{ {
@ -48,12 +48,10 @@ public class HexTile3D : Spatial
} }
private TileType _type; private TileType _type;
public TileType Type public TileType Type
{ {
get get { return _type; }
{
return _type;
}
set set
{ {
@ -61,11 +59,17 @@ public class HexTile3D : Spatial
switch (_type) switch (_type)
{ {
case TileType.Sand: _mesh.SetSurfaceMaterial(0, _sandMaterial); case TileType.Undefined:
_mesh.SetSurfaceMaterial(0, _undefinedMaterial);
break; break;
case TileType.Grass: _mesh.SetSurfaceMaterial(0, _grassMaterial); case TileType.Sand:
_mesh.SetSurfaceMaterial(0, _sandMaterial);
break; break;
case TileType.DeepGrass: _mesh.SetSurfaceMaterial(0, _deepGrassMaterial); case TileType.Grass:
_mesh.SetSurfaceMaterial(0, _grassMaterial);
break;
case TileType.DeepGrass:
_mesh.SetSurfaceMaterial(0, _deepGrassMaterial);
break; break;
default: default:
GD.Print("Invalid tile type: " + value.ToString()); GD.Print("Invalid tile type: " + value.ToString());
@ -89,15 +93,17 @@ public class HexTile3D : Spatial
_area.Connect("mouse_entered", this, nameof(OnAreaMouseEntered)); _area.Connect("mouse_entered", this, nameof(OnAreaMouseEntered));
_area.Connect("mouse_exited", this, nameof(OnAreaMouseExited)); _area.Connect("mouse_exited", this, nameof(OnAreaMouseExited));
_undefinedMaterial = GD.Load<SpatialMaterial>("res://materials/UndefinedTile.tres");
_sandMaterial = GD.Load<SpatialMaterial>("res://materials/SandTile.tres"); _sandMaterial = GD.Load<SpatialMaterial>("res://materials/SandTile.tres");
_grassMaterial = GD.Load<SpatialMaterial>("res://materials/GrassTile.tres"); _grassMaterial = GD.Load<SpatialMaterial>("res://materials/GrassTile.tres");
_deepGrassMaterial = GD.Load<SpatialMaterial>("res://materials/DeepGrassTile.tres"); _deepGrassMaterial = GD.Load<SpatialMaterial>("res://materials/DeepGrassTile.tres");
this.Type = TileType.Grass; this.Type = TileType.Undefined;
} }
public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal, int shapeIndex) public void OnAreaInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
int shapeIndex)
{ {
if (IsMouseOver && inputEvent is InputEventMouseButton) if (IsMouseOver && inputEvent is InputEventMouseButton)
{ {
@ -131,4 +137,3 @@ public class HexTile3D : Spatial
// //
// } // }
} }

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=5 format=2] [gd_scene load_steps=5 format=2]
[ext_resource path="res://scenes/HexTile3D.cs" type="Script" id=1] [ext_resource path="res://scenes/HexTile3D.cs" type="Script" id=1]
[ext_resource path="res://materials/DeepGrassTile.tres" type="Material" id=2] [ext_resource path="res://materials/UndefinedTile.tres" type="Material" id=2]
[sub_resource type="CylinderMesh" id=6] [sub_resource type="CylinderMesh" id=6]
top_radius = 0.5 top_radius = 0.5

View File

@ -5,20 +5,20 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using GoDotLog; using GoDotLog;
//using Dictionary = Godot.Collections.Dictionary;
public class StreamContainer : Spatial public class StreamContainer : Spatial
{ {
// scene nodes // scene nodes
private MeshInstance _bounds; private MeshInstance _bounds;
private Spatial _activeTiles; private Spatial _activeTiles;
private Queue<HexTile3D> _unusedTiles; private Queue<HexTile3D> _unusedTiles;
private TileWorld _tileWorld;
// resources // resources
private PackedScene _hexTileScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn"); private PackedScene _hexTileScene = GD.Load<PackedScene>("res://scenes/HexTile3D.tscn");
// exports // exports
[Export] public Vector2 Dimensions = new Vector2(8, 4); [Export] public Vector2 Dimensions = new Vector2(8, 4);
[Export] public NodePath World;
[Signal] [Signal]
delegate void TileSelected(HexTile3D tile3d); delegate void TileSelected(HexTile3D tile3d);
@ -28,7 +28,6 @@ public class StreamContainer : Spatial
private Rect2 _currentOffsetCoordRect; private Rect2 _currentOffsetCoordRect;
private Rect2 _oldOffsetCoordRect; private Rect2 _oldOffsetCoordRect;
private HexGrid _hexGrid; private HexGrid _hexGrid;
private Random _tileTypeRandom;
private Dictionary<Vector2, HexTile3D> _coordToTile = new Dictionary<Vector2, HexTile3D>(); private Dictionary<Vector2, HexTile3D> _coordToTile = new Dictionary<Vector2, HexTile3D>();
public List<Vector2> RemovedCoords = new List<Vector2>(); public List<Vector2> RemovedCoords = new List<Vector2>();
@ -39,19 +38,25 @@ public class StreamContainer : Spatial
get { return _currentOffsetCoordRect; } get { return _currentOffsetCoordRect; }
} }
public void SetWorld(TileWorld tileWorld)
{
_tileWorld = tileWorld;
}
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
public override void _Ready() public override void _Ready()
{ {
_unusedTiles = new Queue<HexTile3D>(); _unusedTiles = new Queue<HexTile3D>();
_bounds = GetNode<MeshInstance>("Bounds"); _bounds = GetNode<MeshInstance>("Bounds");
_activeTiles = GetNode<Spatial>("ActiveTiles"); _activeTiles = GetNode<Spatial>("ActiveTiles");
_tileTypeRandom = new Random();
_hexGrid = new HexGrid(); _hexGrid = new HexGrid();
Transform boundsTransform = Transform.Identity; Transform boundsTransform = Transform.Identity;
boundsTransform = boundsTransform.Scaled(new Vector3(Dimensions.x, 1, Dimensions.y)); boundsTransform = boundsTransform.Scaled(new Vector3(Dimensions.x, 1, Dimensions.y));
_bounds.Transform = boundsTransform; _bounds.Transform = boundsTransform;
_tileWorld = GetNode<TileWorld>(World);
} }
@ -131,6 +136,7 @@ public class StreamContainer : Spatial
if (isInOld && !isInCurrent && _coordToTile.Keys.Contains(coord)) if (isInOld && !isInCurrent && _coordToTile.Keys.Contains(coord))
{ {
HexTile3D tile3D = _coordToTile[coord]; HexTile3D tile3D = _coordToTile[coord];
tile3D.Type = HexTile3D.TileType.Undefined;
_unusedTiles.Enqueue(tile3D); _unusedTiles.Enqueue(tile3D);
_coordToTile.Remove(coord); _coordToTile.Remove(coord);
RemovedCoords.Add(coord); RemovedCoords.Add(coord);
@ -163,6 +169,11 @@ public class StreamContainer : Spatial
{ {
HexTile3D tile3D = _unusedTiles.Dequeue(); HexTile3D tile3D = _unusedTiles.Dequeue();
tile3D.OffsetCoords = coord; tile3D.OffsetCoords = coord;
tile3D.Type = _tileWorld.GetTileTypeAtOffset(coord);
Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(coord);
tile3D.Transform = tileTransform;
_coordToTile[coord] = tile3D; _coordToTile[coord] = tile3D;
} }
} }
@ -180,10 +191,10 @@ public class StreamContainer : Spatial
tile3D.Connect("TileSelected", this, nameof(OnTileClicked)); tile3D.Connect("TileSelected", this, nameof(OnTileClicked));
Transform tileTransform = tile3D.Transform; Transform tileTransform = tile3D.Transform;
tileTransform.origin.y = GD.Randf() * 0.2f; tileTransform.origin.y = _tileWorld.GetHeightAtOffset(offsetCoords);
tile3D.Transform = tileTransform; tile3D.Transform = tileTransform;
tile3D.Type = _tileWorld.GetTileTypeAtOffset(offsetCoords);
tile3D.Type = HexTile3D.ValidTileTypes[_tileTypeRandom.Next(HexTile3D.ValidTileTypes.Length)];
_coordToTile[offsetCoords] = tile3D; _coordToTile[offsetCoords] = tile3D;
} }

125
scenes/TileWorld.cs Normal file
View File

@ -0,0 +1,125 @@
using Godot;
using System;
using System.Linq;
public class TileWorld : Spatial
{
// signals
[Signal]
delegate void WorldGenerated();
// public members
public Image Heightmap;
public Vector2 Size = new Vector2(100, 100);
public float HeightScale = 10;
// private members
private HexGrid _hexGrid;
private Random _tileTypeRandom;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
_hexGrid = new HexGrid();
_tileTypeRandom = new Random();
}
public void Generate()
{
// GenerateSimpleMap();
GenerateNoiseMap();
EmitSignal("WorldGenerated");
}
private void GenerateSimpleMap()
{
Heightmap = new Image();
Heightmap.Create((int)Size.x, (int)Size.y, false, Image.Format.Rf);
Heightmap.Lock();
foreach (int coord_x in Enumerable.Range(0, (int)Size.x))
{
foreach (int coord_y in Enumerable.Range(0, (int)Size.y))
{
SetHeightAtOffset(new Vector2(coord_x, coord_y), 0.5f);
}
}
}
private void GenerateNoiseMap()
{
Heightmap = new Image();
Heightmap.Create((int)Size.x, (int)Size.y, false, Image.Format.Rf);
NoiseTexture noise_texture = new NoiseTexture();
OpenSimplexNoise noise_generator = new OpenSimplexNoise();
noise_generator.Seed = -1626828106;
noise_generator.Octaves = 3;
noise_generator.Period = 5;
noise_generator.Persistence = 0.5f;
noise_generator.Lacunarity = 2;
Heightmap.CopyFrom(noise_generator.GetImage((int)Size.x, (int)Size.y, null));
}
private void ApplyHeightMap()
{
}
public bool IsOffsetCoordValid(Vector2 offset_coord)
{
return ((int)Math.Clamp(offset_coord.x, 0, Size.x) == (int)offset_coord.x
&& (int)Math.Clamp(offset_coord.y, 0, Size.y) == (int)offset_coord.y);
}
public HexTile3D.TileType GetTileTypeAtOffset(Vector2 offset_coord)
{
if (!IsOffsetCoordValid(offset_coord))
{
return HexTile3D.TileType.Undefined;
}
return HexTile3D.ValidTileTypes[_tileTypeRandom.Next(HexTile3D.ValidTileTypes.Length)];
}
public void SetHeightAtOffset(Vector2 offset_coord, float height)
{
if (!IsOffsetCoordValid(offset_coord))
{
return;
}
Heightmap.SetPixel((int) offset_coord.x, (int) offset_coord.y, new Color(height / HeightScale, 0f, 0f));
}
public float GetHeightAtOffset(Vector2 offset_coord)
{
if (!IsOffsetCoordValid(offset_coord))
{
return -0.1f;
}
return Heightmap.GetPixel((int)offset_coord.x, (int)offset_coord.y).r * HeightScale - HeightScale * 0.5f ;
}
public Vector2 WorldToOffsetCoords(Vector3 world_coord)
{
return _hexGrid.GetHexAt(new Vector2(world_coord.x, world_coord.z)).OffsetCoords;
}
// // Called every frame. 'delta' is the elapsed time since the previous frame.
// public override void _Process(float delta)
// {
//
// }
}

View File

@ -17,7 +17,7 @@ public class HexGridTests : TestClass
Debug.Assert(grid.GetHexAt(new Vector2(0, 0)).AxialCoords == new Vector2(0, 0)); Debug.Assert(grid.GetHexAt(new Vector2(0, 0)).AxialCoords == new Vector2(0, 0));
Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, 0)).AxialCoords == new Vector2(0, 0)); Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, 0)).AxialCoords == new Vector2(0, 0));
Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, -h/2)).AxialCoords == new Vector2(1, 0)); Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, -h / 2)).AxialCoords == new Vector2(1, 0));
Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, h/2)).AxialCoords == new Vector2(1, -1)); Debug.Assert(grid.GetHexAt(new Vector2(w / 2 - 0.01f, h / 2)).AxialCoords == new Vector2(1, -1));
} }
} }