Started working on navigation / editing sandbox.
parent
aaf9c85be7
commit
1397c0a7eb
|
@ -11,7 +11,7 @@ using GoDotLog;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class NavigationComponent : Node
|
public class NavigationComponent : Spatial
|
||||||
{
|
{
|
||||||
public class NavigationPoint
|
public class NavigationPoint
|
||||||
{
|
{
|
||||||
|
@ -83,6 +83,9 @@ public class NavigationComponent : Node
|
||||||
private Vector3 _currentGoalPositionWorld = Vector3.Zero;
|
private Vector3 _currentGoalPositionWorld = Vector3.Zero;
|
||||||
private Quat _currentGoalOrientationWorld = Quat.Identity;
|
private Quat _currentGoalOrientationWorld = Quat.Identity;
|
||||||
|
|
||||||
|
private Area _pathCollisionQueryVolume;
|
||||||
|
private CollisionShape _pathCollisionQueryShape;
|
||||||
|
|
||||||
private List<NavigationPoint> _pathWorldNavigationPoints;
|
private List<NavigationPoint> _pathWorldNavigationPoints;
|
||||||
private HexCell[] _path;
|
private HexCell[] _path;
|
||||||
|
|
||||||
|
@ -90,6 +93,12 @@ public class NavigationComponent : Node
|
||||||
{
|
{
|
||||||
base._Ready();
|
base._Ready();
|
||||||
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
_pathWorldNavigationPoints = new List<NavigationPoint>();
|
||||||
|
|
||||||
|
_pathCollisionQueryVolume = (Area)FindNode("PathCollisionQueryVolume");
|
||||||
|
Debug.Assert(_pathCollisionQueryVolume != null);
|
||||||
|
_pathCollisionQueryShape = (CollisionShape)_pathCollisionQueryVolume.FindNode("CollisionShape");
|
||||||
|
Debug.Assert(_pathCollisionQueryShape != null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void _Process(float delta)
|
public override void _Process(float delta)
|
||||||
|
@ -158,6 +167,8 @@ public class NavigationComponent : Node
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckPathCollision(fromTransformWorld.origin, navigationPoint.WorldPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,15 +189,31 @@ public class NavigationComponent : Node
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool SweptSphereHasCollision(Vector3 fromPosition, Vector3 toPosition, float radius)
|
bool CheckPathCollision(Vector3 fromPositionWorld, Vector3 toPositionWorld)
|
||||||
{
|
{
|
||||||
if ((fromPosition - toPosition).LengthSquared() < 0.001)
|
Vector3 fromPositionLocal = GlobalTransform.XformInv(fromPositionWorld);
|
||||||
|
Vector3 toPositionLocal = GlobalTransform.XformInv(toPositionWorld);
|
||||||
|
float distance = (toPositionLocal - fromPositionLocal).Length();
|
||||||
|
Vector3 direction = (toPositionLocal - fromPositionLocal) / distance;
|
||||||
|
Vector3 side = Vector3.Up.Cross(direction);
|
||||||
|
Basis orientation = new Basis(side, Vector3.Up, direction);
|
||||||
|
|
||||||
|
_pathCollisionQueryVolume.Transform =
|
||||||
|
new Transform(orientation, 0.5f * toPositionLocal + 0.5f * fromPositionLocal).Scaled(new Vector3(0.5f, 0.5f, distance));
|
||||||
|
|
||||||
|
var collisionBodies = _pathCollisionQueryVolume.GetOverlappingBodies();
|
||||||
|
|
||||||
|
if (collisionBodies.Count > 0)
|
||||||
|
{
|
||||||
|
GD.Print("There is a body: " + collisionBodies[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fromPositionWorld - toPositionWorld).LengthSquared() < 0.001)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3 direction = (toPosition - fromPosition).Normalized();
|
|
||||||
|
|
||||||
// TODO: Complete Implementation
|
// TODO: Complete Implementation
|
||||||
Debug.Assert(false);
|
Debug.Assert(false);
|
||||||
return true;
|
return true;
|
||||||
|
@ -206,7 +233,7 @@ public class NavigationComponent : Node
|
||||||
Vector3 startPoint = navigationPoints[startIndex].WorldPosition;
|
Vector3 startPoint = navigationPoints[startIndex].WorldPosition;
|
||||||
Vector3 endPoint = navigationPoints[endIndex].WorldPosition;
|
Vector3 endPoint = navigationPoints[endIndex].WorldPosition;
|
||||||
|
|
||||||
if (SweptSphereHasCollision(startPoint, endPoint, 0.25f))
|
if (CheckPathCollision(startPoint, endPoint))
|
||||||
{
|
{
|
||||||
smoothedPath.Add(navigationPoints[endIndex-1]);
|
smoothedPath.Add(navigationPoints[endIndex-1]);
|
||||||
smoothedPath.Add(navigationPoints[endIndex]);
|
smoothedPath.Add(navigationPoints[endIndex]);
|
||||||
|
|
|
@ -10,6 +10,8 @@ using GodotComponentTest.utils;
|
||||||
public class Player : Entity, IInteractionInterface
|
public class Player : Entity, IInteractionInterface
|
||||||
{
|
{
|
||||||
// public members
|
// public members
|
||||||
|
[Export] public NodePath TileWorldNode;
|
||||||
|
|
||||||
public Vector3 TargetPosition = Vector3.Zero;
|
public Vector3 TargetPosition = Vector3.Zero;
|
||||||
public int goldCount = 0;
|
public int goldCount = 0;
|
||||||
|
|
||||||
|
@ -47,7 +49,7 @@ public class Player : Entity, IInteractionInterface
|
||||||
_groundMotion = new GroundMotionComponent();
|
_groundMotion = new GroundMotionComponent();
|
||||||
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
|
_worldInfo = (WorldInfoComponent)FindNode("WorldInfo", false);
|
||||||
_navigationComponent = (NavigationComponent)FindNode("Navigation", false);
|
_navigationComponent = (NavigationComponent)FindNode("Navigation", false);
|
||||||
_navigationComponent.TileWorld = _worldInfo.TileWorld;
|
_navigationComponent.TileWorld = GetNode<TileWorld>(TileWorldNode);
|
||||||
TaskQueueComponent = new TaskQueueComponent();
|
TaskQueueComponent = new TaskQueueComponent();
|
||||||
|
|
||||||
_itemAttractorArea = (Area)FindNode("ItemAttractorArea", false);
|
_itemAttractorArea = (Area)FindNode("ItemAttractorArea", false);
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
[gd_scene load_steps=15 format=2]
|
[gd_scene load_steps=12 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://scenes/StreamContainer.cs" type="Script" id=1]
|
[ext_resource path="res://scenes/StreamContainer.tscn" type="PackedScene" id=1]
|
||||||
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=2]
|
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=2]
|
||||||
|
[ext_resource path="res://scenes/Camera.tscn" type="PackedScene" id=3]
|
||||||
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=5]
|
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=5]
|
||||||
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=7]
|
[ext_resource path="res://entities/Chest.tscn" type="PackedScene" id=7]
|
||||||
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=8]
|
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=8]
|
||||||
[ext_resource path="res://scenes/Game.cs" type="Script" id=9]
|
[ext_resource path="res://scenes/Game.cs" type="Script" id=9]
|
||||||
[ext_resource path="res://scenes/DebugCamera.gd" type="Script" id=10]
|
|
||||||
[ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12]
|
[ext_resource path="res://ui/WorldGeneratorUI.gd" type="Script" id=12]
|
||||||
[ext_resource path="res://entities/Axe.tscn" type="PackedScene" id=14]
|
[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://systems/InteractionSystem.cs" type="Script" id=15]
|
||||||
|
@ -27,16 +27,6 @@ tracks/0/keys = {
|
||||||
"values": [ Vector2( 1, 1 ), Vector2( 2, 2 ), Vector2( 1, 1 ) ]
|
"values": [ Vector2( 1, 1 ), Vector2( 2, 2 ), Vector2( 1, 1 ) ]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="CubeMesh" id=1]
|
|
||||||
size = Vector3( 1, 1, 1 )
|
|
||||||
|
|
||||||
[sub_resource type="SpatialMaterial" id=2]
|
|
||||||
params_blend_mode = 3
|
|
||||||
albedo_color = Color( 1, 1, 1, 0.156863 )
|
|
||||||
|
|
||||||
[sub_resource type="BoxShape" id=9]
|
|
||||||
extents = Vector3( 20, 1, 20 )
|
|
||||||
|
|
||||||
[node name="Game" type="Spatial"]
|
[node name="Game" type="Spatial"]
|
||||||
script = ExtResource( 9 )
|
script = ExtResource( 9 )
|
||||||
|
|
||||||
|
@ -48,6 +38,7 @@ visible = false
|
||||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0 )
|
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0 )
|
||||||
|
|
||||||
[node name="TileWorld" parent="." instance=ExtResource( 8 )]
|
[node name="TileWorld" parent="." instance=ExtResource( 8 )]
|
||||||
|
GenerationMapType = 0
|
||||||
Size = 128
|
Size = 128
|
||||||
|
|
||||||
[node name="GameUI" type="HBoxContainer" parent="."]
|
[node name="GameUI" type="HBoxContainer" parent="."]
|
||||||
|
@ -282,37 +273,15 @@ rect_min_size = Vector2( 100, 100 )
|
||||||
stretch_mode = 1
|
stretch_mode = 1
|
||||||
flip_v = true
|
flip_v = true
|
||||||
|
|
||||||
[node name="StreamContainer" type="Spatial" parent="."]
|
[node name="StreamContainer" parent="." instance=ExtResource( 1 )]
|
||||||
transform = Transform( 1, 0, 0, 0, 1, 2.98023e-08, 0, -2.98023e-08, 1, 0, 0, -4.76837e-07 )
|
|
||||||
script = ExtResource( 1 )
|
|
||||||
Dimensions = Vector2( 35, 30 )
|
|
||||||
World = NodePath("../TileWorld")
|
|
||||||
|
|
||||||
[node name="ActiveTiles" type="Spatial" parent="StreamContainer"]
|
[node name="Camera" parent="." instance=ExtResource( 3 )]
|
||||||
|
|
||||||
[node name="Bounds" type="MeshInstance" parent="StreamContainer"]
|
|
||||||
transform = Transform( 4, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0 )
|
|
||||||
visible = false
|
|
||||||
mesh = SubResource( 1 )
|
|
||||||
skeleton = NodePath("../..")
|
|
||||||
material/0 = SubResource( 2 )
|
|
||||||
|
|
||||||
[node name="Area" type="Area" parent="StreamContainer"]
|
|
||||||
|
|
||||||
[node name="CollisionShape" type="CollisionShape" parent="StreamContainer/Area"]
|
|
||||||
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0 )
|
|
||||||
shape = SubResource( 9 )
|
|
||||||
|
|
||||||
[node name="Camera" type="Camera" parent="."]
|
|
||||||
transform = Transform( 1, 0, 0, 0, 0.60042, 0.799685, 0, -0.799685, 0.60042, -4.76837e-07, 6.37557, 4.57224 )
|
|
||||||
current = true
|
|
||||||
fov = 60.0
|
|
||||||
script = ExtResource( 10 )
|
|
||||||
|
|
||||||
[node name="InteractionSystem" type="Node" parent="."]
|
[node name="InteractionSystem" type="Node" parent="."]
|
||||||
script = ExtResource( 15 )
|
script = ExtResource( 15 )
|
||||||
|
|
||||||
[node name="Player" parent="." instance=ExtResource( 2 )]
|
[node name="Player" parent="." instance=ExtResource( 2 )]
|
||||||
|
TileWorldNode = NodePath("../TileWorld")
|
||||||
|
|
||||||
[node name="Entities" type="Spatial" parent="."]
|
[node name="Entities" type="Spatial" parent="."]
|
||||||
|
|
||||||
|
|
|
@ -226,13 +226,11 @@ public class StreamContainer : Spatial
|
||||||
|
|
||||||
public void OnTileClicked(HexTile3D tile)
|
public void OnTileClicked(HexTile3D tile)
|
||||||
{
|
{
|
||||||
// GD.Print("Clicked on Tile at " + tile.OffsetCoords);
|
|
||||||
EmitSignal("TileClicked", tile);
|
EmitSignal("TileClicked", tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnTileHovered(HexTile3D tile)
|
public void OnTileHovered(HexTile3D tile)
|
||||||
{
|
{
|
||||||
// GD.Print("Hovered on Tile at " + tile.OffsetCoords);
|
|
||||||
EmitSignal("TileHovered", tile);
|
EmitSignal("TileHovered", tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
extends Spatial
|
|
||||||
|
|
||||||
onready var hexgrid = preload("res://addons/gdhexgrid/HexGrid.gd").new()
|
|
||||||
onready var hextile3d = preload("res://scenes/HexTile3D.tscn")
|
|
||||||
onready var active_tiles = $ActiveTiles
|
|
||||||
|
|
||||||
|
|
||||||
export var world_rect: Rect2 = Rect2() setget set_rect
|
|
||||||
var offset_coord_rect: Rect2 = Rect2()
|
|
||||||
|
|
||||||
var bottom_left_cell: HexCell = HexCell.new()
|
|
||||||
var top_right_cell: HexCell = HexCell.new()
|
|
||||||
|
|
||||||
var tiles = {}
|
|
||||||
var tiles_by_offset_coord = {}
|
|
||||||
|
|
||||||
onready var bounds = $Bounds
|
|
||||||
|
|
||||||
|
|
||||||
func _ready():
|
|
||||||
set_rect(world_rect)
|
|
||||||
|
|
||||||
|
|
||||||
func is_hex_coord_in_rect (coord: Vector2):
|
|
||||||
var rect_end = offset_coord_rect.end
|
|
||||||
return coord.x >= offset_coord_rect.position.x and coord.x < offset_coord_rect.end.x and coord.y >= offset_coord_rect.position.y and coord.y < offset_coord_rect.end.y
|
|
||||||
|
|
||||||
|
|
||||||
func instantiate_hextile3d():
|
|
||||||
return hextile3d.instance()
|
|
||||||
|
|
||||||
|
|
||||||
func add_hextile_to_tree(hextile3d):
|
|
||||||
active_tiles.add_child(hextile3d)
|
|
||||||
|
|
||||||
|
|
||||||
func create_hextile3d_at (coord: Vector2) -> HexTile3D:
|
|
||||||
if not coord in tiles_by_offset_coord.keys():
|
|
||||||
var new_hextile3d = instantiate_hextile3d()
|
|
||||||
add_hextile_to_tree(new_hextile3d)
|
|
||||||
new_hextile3d.game_tile.offset_coords = coord
|
|
||||||
new_hextile3d.transform.origin.y = -9999
|
|
||||||
tiles_by_offset_coord[coord] = new_hextile3d
|
|
||||||
|
|
||||||
return tiles_by_offset_coord[coord]
|
|
||||||
|
|
||||||
|
|
||||||
func cleanup_tiles():
|
|
||||||
var num_deleted = 0
|
|
||||||
var children = active_tiles.get_children()
|
|
||||||
for child in children:
|
|
||||||
var tile_offset_coords = child.game_tile.offset_coords
|
|
||||||
if not is_hex_coord_in_rect(tile_offset_coords):
|
|
||||||
tiles_by_offset_coord.erase(tile_offset_coords)
|
|
||||||
child.queue_free()
|
|
||||||
active_tiles.remove_child(child)
|
|
||||||
num_deleted = num_deleted + 1
|
|
||||||
|
|
||||||
# print ("deleted ", num_deleted, " tiles")
|
|
||||||
|
|
||||||
|
|
||||||
func set_rect(rect: Rect2):
|
|
||||||
world_rect = rect
|
|
||||||
|
|
||||||
if bounds == null:
|
|
||||||
return
|
|
||||||
|
|
||||||
var world_rect_center = rect.get_center()
|
|
||||||
|
|
||||||
bounds.transform = Transform(
|
|
||||||
Vector3 (world_rect.size.x / 2, 0, 0),
|
|
||||||
Vector3(0, 1, 0), Vector3(0, 0, world_rect.size.y / 2),
|
|
||||||
Vector3(world_rect_center.x, 0, world_rect_center.y)
|
|
||||||
)
|
|
||||||
|
|
||||||
bottom_left_cell = hexgrid.get_hex_at(Vector2(rect.position.x, rect.end.y))
|
|
||||||
top_right_cell = hexgrid.get_hex_at(Vector2(rect.end.x, rect.position.y))
|
|
||||||
|
|
||||||
offset_coord_rect = Rect2(
|
|
||||||
bottom_left_cell.offset_coords.x, bottom_left_cell.offset_coords.y,
|
|
||||||
top_right_cell.offset_coords.x - bottom_left_cell.offset_coords.x, top_right_cell.offset_coords.y - bottom_left_cell.offset_coords.y
|
|
||||||
)
|
|
||||||
|
|
||||||
# print ("----")
|
|
||||||
# print ("world_rect: ", world_rect)
|
|
||||||
# print ("cells: ", bottom_left_cell.offset_coords, " to ", top_right_cell.offset_coords)
|
|
||||||
# print ("offset_coord_rect: ", offset_coord_rect.position, " size: ", offset_coord_rect.size)
|
|
||||||
|
|
||||||
cleanup_tiles()
|
|
||||||
|
|
||||||
return world_rect
|
|
|
@ -3,6 +3,7 @@ using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using Godot.Collections;
|
using Godot.Collections;
|
||||||
|
using Namespace;
|
||||||
using Array = Godot.Collections.Array;
|
using Array = Godot.Collections.Array;
|
||||||
using Vector2 = Godot.Vector2;
|
using Vector2 = Godot.Vector2;
|
||||||
using Vector3 = Godot.Vector3;
|
using Vector3 = Godot.Vector3;
|
||||||
|
@ -28,7 +29,18 @@ public class TileWorld : Spatial
|
||||||
delegate void WorldGenerated();
|
delegate void WorldGenerated();
|
||||||
|
|
||||||
// public members
|
// public members
|
||||||
|
public enum MapType
|
||||||
|
{
|
||||||
|
Noise,
|
||||||
|
Debug,
|
||||||
|
Flat,
|
||||||
|
}
|
||||||
|
|
||||||
|
[ExportFlagsEnum(typeof(MapType))] public MapType GenerationMapType = MapType.Debug;
|
||||||
|
|
||||||
[Export] public int Size = 64;
|
[Export] public int Size = 64;
|
||||||
|
[Export] public bool DebugMap = false;
|
||||||
|
|
||||||
public float HeightScale = 2.0f;
|
public float HeightScale = 2.0f;
|
||||||
public Image Heightmap;
|
public Image Heightmap;
|
||||||
public Image Colormap;
|
public Image Colormap;
|
||||||
|
@ -100,10 +112,15 @@ public class TileWorld : Spatial
|
||||||
|
|
||||||
OnMapGenerationStart();
|
OnMapGenerationStart();
|
||||||
|
|
||||||
GenerateNoiseMap();
|
switch (GenerationMapType)
|
||||||
// GenerateDebugMap();
|
{
|
||||||
|
case MapType.Debug: GenerateDebugMap();
|
||||||
OnHeightMapChanged();
|
break;
|
||||||
|
case MapType.Flat: GenerateFlatMap();
|
||||||
|
break;
|
||||||
|
case MapType.Noise: GenerateNoiseMap();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateDebugMap()
|
private void GenerateDebugMap()
|
||||||
|
@ -128,7 +145,35 @@ public class TileWorld : Spatial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Colormap.SetPixel(Size - 1, Size - 1, new Color(1, 1, 1, 1));
|
// Colormap.SetPixel(Size - 1, Size - 1, new Color(1, 1, 1, 1));
|
||||||
|
Colormap.Fill(Colors.ForestGreen);
|
||||||
|
Colormap.Unlock();
|
||||||
|
|
||||||
|
OnMapGenerationComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void GenerateFlatMap()
|
||||||
|
{
|
||||||
|
Colormap = new Image();
|
||||||
|
Colormap.Create(Size, Size, false, Image.Format.Rgba8);
|
||||||
|
|
||||||
|
Heightmap = new Image();
|
||||||
|
Heightmap.Create(Size, Size, false, Image.Format.Rf);
|
||||||
|
|
||||||
|
Heightmap.Lock();
|
||||||
|
Colormap.Lock();
|
||||||
|
|
||||||
|
foreach (int coord_x in Enumerable.Range(0, Size))
|
||||||
|
{
|
||||||
|
foreach (int coord_y in Enumerable.Range(0, Size))
|
||||||
|
{
|
||||||
|
Heightmap.SetPixel(coord_x, coord_y,
|
||||||
|
new Color(0, 0, 0, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Colormap.Fill(Colors.ForestGreen);
|
||||||
Colormap.Unlock();
|
Colormap.Unlock();
|
||||||
|
|
||||||
OnMapGenerationComplete();
|
OnMapGenerationComplete();
|
||||||
|
@ -163,7 +208,7 @@ public class TileWorld : Spatial
|
||||||
{
|
{
|
||||||
child.QueueFree();
|
child.QueueFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Node child in Entities.GetChildren())
|
foreach (Node child in Entities.GetChildren())
|
||||||
{
|
{
|
||||||
child.QueueFree();
|
child.QueueFree();
|
||||||
|
@ -329,6 +374,17 @@ public class TileWorld : Spatial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetTileColorAtOffset(Vector2 offsetCoord, Color color)
|
||||||
|
{
|
||||||
|
Vector2 textureCoord = OffsetToTextureCoord(offsetCoord);
|
||||||
|
|
||||||
|
Colormap.Lock();
|
||||||
|
Colormap.SetPixel((int) textureCoord.x, (int) textureCoord.y, color);
|
||||||
|
Colormap.Unlock();
|
||||||
|
|
||||||
|
EmitSignal("WorldGenerated");
|
||||||
|
}
|
||||||
|
|
||||||
public Vector2 WorldToOffsetCoords(Vector3 worldCoord)
|
public Vector2 WorldToOffsetCoords(Vector3 worldCoord)
|
||||||
{
|
{
|
||||||
return _hexGrid.GetHexAt(new Vector2(worldCoord.x, worldCoord.z)).OffsetCoords;
|
return _hexGrid.GetHexAt(new Vector2(worldCoord.x, worldCoord.z)).OffsetCoords;
|
||||||
|
@ -337,13 +393,9 @@ public class TileWorld : Spatial
|
||||||
|
|
||||||
public Vector3 GetTileWorldCenterFromOffset(Vector2 offsetCoord)
|
public Vector3 GetTileWorldCenterFromOffset(Vector2 offsetCoord)
|
||||||
{
|
{
|
||||||
Vector2 tileCenter = _hexGrid.GetHexCenterFromOffset(offsetCoord);
|
Vector2 tileCenter = _hexGrid.GetHexCenterFromOffset(offsetCoord - Vector2.One * Mathf.Round(Size / 2f));
|
||||||
|
|
||||||
// TODO: coordinates do not match for bigger maps
|
return new Vector3(tileCenter.x, GetHeightAtOffset(offsetCoord), tileCenter.y);
|
||||||
return new Vector3(
|
|
||||||
tileCenter.x - Mathf.Round(Size * 0.75f * 0.5f),
|
|
||||||
GetHeightAtOffset(offsetCoord),
|
|
||||||
tileCenter.y + ((Mathf.Sqrt(3) / 2) * Mathf.Round(Size * 0.5f)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class EditorUI : Control
|
||||||
|
{
|
||||||
|
// exported members
|
||||||
|
[Export] public NodePath World;
|
||||||
|
[Export] public NodePath StreamContainer;
|
||||||
|
|
||||||
|
// public members
|
||||||
|
public Vector2 currentTileOffset = Vector2.Zero;
|
||||||
|
|
||||||
|
// private members
|
||||||
|
private Button _resetButton;
|
||||||
|
private Button _grassButton;
|
||||||
|
private Button _sandButton;
|
||||||
|
private Button _waterButton;
|
||||||
|
|
||||||
|
private TileWorld _tileWorld;
|
||||||
|
private StreamContainer _streamContainer;
|
||||||
|
|
||||||
|
private enum TileType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Grass,
|
||||||
|
Sand,
|
||||||
|
Water
|
||||||
|
}
|
||||||
|
private TileType _currentTileType = TileType.None;
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_tileWorld = (TileWorld) GetNode(World);
|
||||||
|
_streamContainer = (StreamContainer)GetNode(StreamContainer);
|
||||||
|
|
||||||
|
// signals
|
||||||
|
_resetButton = (Button) FindNode("ResetButton");
|
||||||
|
_resetButton.Connect("pressed", this, nameof(OnResetButton));
|
||||||
|
|
||||||
|
_grassButton = (Button) FindNode("GrassButton");
|
||||||
|
_grassButton.Connect("pressed", this, nameof(OnGrassButton));
|
||||||
|
|
||||||
|
_sandButton = (Button) FindNode("SandButton");
|
||||||
|
_sandButton.Connect("pressed", this, nameof(OnSandButton));
|
||||||
|
|
||||||
|
_waterButton = (Button) FindNode("WaterButton");
|
||||||
|
_waterButton.Connect("pressed", this, nameof(OnWaterButton));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void OnResetButton()
|
||||||
|
{
|
||||||
|
GD.Print("Resetting Map");
|
||||||
|
_tileWorld.Seed = _tileWorld.Seed + 1;
|
||||||
|
_tileWorld.Generate(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGrassButton()
|
||||||
|
{
|
||||||
|
_currentTileType = TileType.Grass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSandButton()
|
||||||
|
{
|
||||||
|
_currentTileType = TileType.Sand;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnWaterButton()
|
||||||
|
{
|
||||||
|
_currentTileType = TileType.Water;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnTileClicked(Vector2 offsetCoord)
|
||||||
|
{
|
||||||
|
switch (_currentTileType)
|
||||||
|
{
|
||||||
|
case TileType.Grass:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Green);
|
||||||
|
break;
|
||||||
|
case TileType.Water:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Blue);
|
||||||
|
break;
|
||||||
|
case TileType.Sand:_tileWorld.SetTileColorAtOffset(currentTileOffset, Colors.Yellow);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
public class NavigationTests : Spatial
|
||||||
|
{
|
||||||
|
// Declare member variables here. Examples:
|
||||||
|
// private int a = 2;
|
||||||
|
// private string b = "text";
|
||||||
|
|
||||||
|
private StaticBody _groundLayer;
|
||||||
|
private HexGrid _hexGrid;
|
||||||
|
private HexCell _currentTile;
|
||||||
|
private HexCell _lastTile;
|
||||||
|
|
||||||
|
private Spatial _mouseHighlight;
|
||||||
|
private ShaderMaterial _tileMaterial;
|
||||||
|
|
||||||
|
private EditorUI _editorUi;
|
||||||
|
private TileWorld _tileWorld;
|
||||||
|
private StreamContainer _streamContainer;
|
||||||
|
private Player _player;
|
||||||
|
|
||||||
|
// Called when the node enters the scene tree for the first time.
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_hexGrid = new HexGrid();
|
||||||
|
_currentTile = new HexCell();
|
||||||
|
_lastTile = new HexCell();
|
||||||
|
|
||||||
|
_tileMaterial = GD.Load<ShaderMaterial>("materials/HexTileTextureLookup.tres");
|
||||||
|
|
||||||
|
_groundLayer = GetNode<StaticBody>("GroundLayer");
|
||||||
|
|
||||||
|
_mouseHighlight = GetNode<Spatial>("MouseHighlight");
|
||||||
|
|
||||||
|
_editorUi = GetNode<EditorUI>("EditorUI");
|
||||||
|
_tileWorld = GetNode<TileWorld>("TileWorld");
|
||||||
|
_tileWorld.Connect("WorldGenerated", this, nameof(OnWorldGenerated));
|
||||||
|
_streamContainer = GetNode<StreamContainer>("StreamContainer");
|
||||||
|
|
||||||
|
_streamContainer.SetCenterTile(_currentTile);
|
||||||
|
|
||||||
|
_player = GetNode<Player>("Player");
|
||||||
|
|
||||||
|
// input handling
|
||||||
|
_groundLayer.Connect("input_event", this, nameof(OnGroundLayerInputEvent));
|
||||||
|
_streamContainer.Connect("TileClicked", this, nameof(OnTileClicked));
|
||||||
|
_streamContainer.Connect("TileHovered", this, nameof(OnTileHovered));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnWorldGenerated()
|
||||||
|
{
|
||||||
|
_streamContainer.OnWorldGenerated();
|
||||||
|
|
||||||
|
// Properly place the Player
|
||||||
|
Vector2 centerTileCoord = (Vector2.One * _tileWorld.Size / 2).Round();
|
||||||
|
Vector3 worldCenterTileCoords = _tileWorld.GetTileWorldCenterFromOffset(centerTileCoord);
|
||||||
|
worldCenterTileCoords.y = _tileWorld.GetHeightAtOffset(centerTileCoord);
|
||||||
|
Transform playerTransform = Transform.Identity;
|
||||||
|
playerTransform.origin = worldCenterTileCoords;
|
||||||
|
_player.Transform = playerTransform;
|
||||||
|
|
||||||
|
ImageTexture newWorldTexture = new ImageTexture();
|
||||||
|
newWorldTexture.CreateFromImage(_tileWorld.Colormap,
|
||||||
|
(uint)(Texture.FlagsEnum.Mipmaps | Texture.FlagsEnum.Repeat));
|
||||||
|
_tileMaterial.SetShaderParam("MapAlbedoTexture", newWorldTexture);
|
||||||
|
_tileMaterial.SetShaderParam("TextureSize", (int)_tileWorld.Colormap.GetSize().x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UpdateCurrentTile(HexCell tile)
|
||||||
|
{
|
||||||
|
if (_currentTile.AxialCoords == tile.AxialCoords)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastTile = _currentTile;
|
||||||
|
_currentTile = tile;
|
||||||
|
|
||||||
|
GD.Print("Current tile: " + _currentTile.OffsetCoords);
|
||||||
|
|
||||||
|
if (_lastTile.OffsetCoords != _currentTile.OffsetCoords && _editorUi != null)
|
||||||
|
{
|
||||||
|
_editorUi.currentTileOffset = _currentTile.OffsetCoords;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 planeCoords = _hexGrid.GetHexCenterFromOffset(_currentTile.OffsetCoords);
|
||||||
|
Transform tileTransform = Transform.Identity;
|
||||||
|
tileTransform.origin.x = planeCoords.x;
|
||||||
|
tileTransform.origin.y = _tileWorld.GetHeightAtOffset(_currentTile.OffsetCoords) + 0.1f;
|
||||||
|
tileTransform.origin.z = planeCoords.y;
|
||||||
|
|
||||||
|
_mouseHighlight.Transform = tileTransform;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnGroundLayerInputEvent(Node camera, InputEvent inputEvent, Vector3 position, Vector3 normal,
|
||||||
|
int shapeIndex)
|
||||||
|
{
|
||||||
|
UpdateCurrentTile(_hexGrid.GetHexAt(new Vector2(position.x, position.z)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnTileClicked(HexTile3D tile)
|
||||||
|
{
|
||||||
|
if (_editorUi != null)
|
||||||
|
{
|
||||||
|
_editorUi.OnTileClicked(tile.OffsetCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnTileHovered(HexTile3D tile)
|
||||||
|
{
|
||||||
|
UpdateCurrentTile(tile.Cell);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
[gd_scene load_steps=12 format=2]
|
||||||
|
|
||||||
|
[ext_resource path="res://entities/Player.tscn" type="PackedScene" id=1]
|
||||||
|
[ext_resource path="res://scenes/TileWorld.tscn" type="PackedScene" id=2]
|
||||||
|
[ext_resource path="res://utils/TileHighlight.tscn" type="PackedScene" id=3]
|
||||||
|
[ext_resource path="res://scenes/tests/NavigationTests.cs" type="Script" id=4]
|
||||||
|
[ext_resource path="res://scenes/StreamContainer.tscn" type="PackedScene" id=5]
|
||||||
|
[ext_resource path="res://scenes/Camera.tscn" type="PackedScene" id=6]
|
||||||
|
[ext_resource path="res://scenes/tests/EditorUI.cs" type="Script" id=7]
|
||||||
|
|
||||||
|
[sub_resource type="ButtonGroup" id=4]
|
||||||
|
resource_local_to_scene = false
|
||||||
|
resource_name = "TileTypeButtonGroup"
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape" id=1]
|
||||||
|
extents = Vector3( 50, 1, 50 )
|
||||||
|
|
||||||
|
[sub_resource type="SpatialMaterial" id=3]
|
||||||
|
albedo_color = Color( 0.180392, 0.384314, 0.0235294, 1 )
|
||||||
|
|
||||||
|
[sub_resource type="CubeMesh" id=2]
|
||||||
|
material = SubResource( 3 )
|
||||||
|
|
||||||
|
[node name="NavigationTests" type="Spatial"]
|
||||||
|
script = ExtResource( 4 )
|
||||||
|
|
||||||
|
[node name="EditorUI" type="Control" parent="."]
|
||||||
|
margin_right = 40.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
script = ExtResource( 7 )
|
||||||
|
World = NodePath("../TileWorld")
|
||||||
|
StreamContainer = NodePath("../StreamContainer")
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="EditorUI"]
|
||||||
|
margin_left = 5.0
|
||||||
|
margin_top = 5.0
|
||||||
|
margin_right = 40.0
|
||||||
|
margin_bottom = 40.0
|
||||||
|
|
||||||
|
[node name="ResetButton" type="Button" parent="EditorUI/HBoxContainer"]
|
||||||
|
margin_right = 48.0
|
||||||
|
margin_bottom = 35.0
|
||||||
|
text = "Reset"
|
||||||
|
|
||||||
|
[node name="ModeLabel" type="Label" parent="EditorUI/HBoxContainer"]
|
||||||
|
margin_left = 52.0
|
||||||
|
margin_top = 10.0
|
||||||
|
margin_right = 88.0
|
||||||
|
margin_bottom = 24.0
|
||||||
|
text = "Mode"
|
||||||
|
|
||||||
|
[node name="GrassButton" type="Button" parent="EditorUI/HBoxContainer"]
|
||||||
|
margin_left = 92.0
|
||||||
|
margin_right = 140.0
|
||||||
|
margin_bottom = 35.0
|
||||||
|
toggle_mode = true
|
||||||
|
group = SubResource( 4 )
|
||||||
|
text = "Grass"
|
||||||
|
|
||||||
|
[node name="WaterButton" type="Button" parent="EditorUI/HBoxContainer"]
|
||||||
|
margin_left = 144.0
|
||||||
|
margin_right = 194.0
|
||||||
|
margin_bottom = 35.0
|
||||||
|
toggle_mode = true
|
||||||
|
group = SubResource( 4 )
|
||||||
|
text = "Water"
|
||||||
|
|
||||||
|
[node name="SandButton" type="Button" parent="EditorUI/HBoxContainer"]
|
||||||
|
margin_left = 198.0
|
||||||
|
margin_right = 240.0
|
||||||
|
margin_bottom = 35.0
|
||||||
|
toggle_mode = true
|
||||||
|
group = SubResource( 4 )
|
||||||
|
text = "Sand"
|
||||||
|
|
||||||
|
[node name="Player" parent="." instance=ExtResource( 1 )]
|
||||||
|
TileWorldNode = NodePath("../TileWorld")
|
||||||
|
|
||||||
|
[node name="GroundLayer" type="StaticBody" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape" type="CollisionShape" parent="GroundLayer"]
|
||||||
|
transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0 )
|
||||||
|
shape = SubResource( 1 )
|
||||||
|
|
||||||
|
[node name="MeshInstance" type="MeshInstance" parent="GroundLayer"]
|
||||||
|
transform = Transform( 50, 0, 0, 0, 1, 0, 0, 0, 50, 0, -1.05, 0 )
|
||||||
|
mesh = SubResource( 2 )
|
||||||
|
|
||||||
|
[node name="TileWorld" parent="." instance=ExtResource( 2 )]
|
||||||
|
GenerationMapType = 2
|
||||||
|
Size = 20
|
||||||
|
DebugMap = true
|
||||||
|
|
||||||
|
[node name="MouseHighlight" parent="." instance=ExtResource( 3 )]
|
||||||
|
|
||||||
|
[node name="StreamContainer" parent="." instance=ExtResource( 5 )]
|
||||||
|
|
||||||
|
[node name="Camera" parent="." instance=ExtResource( 6 )]
|
||||||
|
transform = Transform( 1, 0, 0, 0, 0.60042, 0.799685, 0, -0.799685, 0.60042, -4.76837e-07, 9.56665, 7.86873 )
|
||||||
|
|
||||||
|
[editable path="Player"]
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Source: https://gist.github.com/kleonc/a2bab51686ac6f4d7cb28aec88efa5d9
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Namespace
|
||||||
|
{
|
||||||
|
using UnderlyingType = UInt64;
|
||||||
|
|
||||||
|
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
|
||||||
|
public class ExportFlagsEnumAttribute : ExportAttribute
|
||||||
|
{
|
||||||
|
public ExportFlagsEnumAttribute(Type enumType)
|
||||||
|
: base(PropertyHint.Flags, GetFlagsEnumHintString(enumType))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private static string GetFlagsEnumHintString(Type enumType)
|
||||||
|
{
|
||||||
|
Dictionary<UnderlyingType, List<string>> flagNamesByFlag = new Dictionary<UnderlyingType, List<string>>();
|
||||||
|
UnderlyingType flag = (UnderlyingType)1;
|
||||||
|
foreach (string name in Enum.GetNames(enumType))
|
||||||
|
{
|
||||||
|
UnderlyingType value = (UnderlyingType)Convert.ChangeType(Enum.Parse(enumType, name), typeof(UnderlyingType));
|
||||||
|
while (value > flag)
|
||||||
|
{
|
||||||
|
if (!flagNamesByFlag.ContainsKey(flag))
|
||||||
|
{
|
||||||
|
flagNamesByFlag.Add(flag, new List<string>());
|
||||||
|
}
|
||||||
|
flag <<= 1;
|
||||||
|
}
|
||||||
|
if (value == flag)
|
||||||
|
{
|
||||||
|
if (!flagNamesByFlag.TryGetValue(flag, out List<string> names))
|
||||||
|
{
|
||||||
|
names = new List<string>();
|
||||||
|
flagNamesByFlag.Add(flag, names);
|
||||||
|
}
|
||||||
|
names.Add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string.Join(", ", flagNamesByFlag.Values.Select(flagNames => string.Join(" / ", flagNames)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue