Reviewed-on: #16
This commit is contained in:
ktyl 2022-12-13 01:54:54 +01:00
parent 47c2c189b8
commit 58c2d9a451
28 changed files with 491 additions and 181 deletions

32
gdd.md
View File

@ -8,6 +8,11 @@
* Education * Education
* Supply chains * Supply chains
## Clock
Ticks can be applied manually or they can be applied over time.
Add a tick counter as a UI element.
## Grid ## Grid
Add a 10x10 grid. Add a 10x10 grid.
@ -17,13 +22,6 @@ Tiles are highlighted a different colour when the player hovers over them with t
Tiles in the grid contain floating point values. Tiles in the grid contain floating point values.
These values propogate to neighbouring cells a tick happens. These values propogate to neighbouring cells a tick happens.
Ticks can be applied manually or they can be applied over time.
Add a tick counter as a UI element.
* [ ] understand different options for representing the rate of diffusion
Diffusing should use 'pull' operations; tiles should pull values from surrounding tiles.
This is an attempt to think forwards to parallelising computation of diffusiong - ideally using the GPU.
#### Diffusion Algorithm #### Diffusion Algorithm
@ -46,6 +44,20 @@ private float TransferHeat(float t0, float alpha, float nx, float ny, float px,
} }
``` ```
Diffusing uses 'pull' operations; tiles pull values from surrounding tiles.
This is an attempt to think forwards to parallelising computation of diffusiong - ideally using the GPU.
## Overlays
The current overlay can be cycled with a UI button or by pressing `tab`.
Overlays display tile information by displaying tiles in different colours.
Normally, tile colours are displayed depending on their type and health.
This is the 'normal' view with no overlay.
Other overlays include:
* [Heat overlay](#heat-overlay)
## Build Mode ## Build Mode
Build mode can be entered by pressing the `b` key. Build mode can be entered by pressing the `b` key.
@ -71,8 +83,12 @@ In build mode, the player can click on a wile tile to turn it into a developed t
Developed tiles produce heat while wild tiles absorb it. Developed tiles produce heat while wild tiles absorb it.
Every tile has a floating-point heat value which diffuses to its neighbours. Every tile has a floating-point heat value which diffuses to its neighbours.
Add a heat map view which can be toggled or cycled to.
### Heat overlay
A heat map view can be toggled or cycled to.
It is possible to cycle the view using a UI element. It is possible to cycle the view using a UI element.
Tile colours are displayed as a value along a gradient from cold to hot tiles.
## Tile Health ## Tile Health

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=11 format=2] [gd_scene load_steps=13 format=2]
[ext_resource path="res://nodes/grid.tscn" type="PackedScene" id=1] [ext_resource path="res://nodes/grid.tscn" type="PackedScene" id=1]
[ext_resource path="res://nodes/grid_cursor.tscn" type="PackedScene" id=2] [ext_resource path="res://nodes/grid_cursor.tscn" type="PackedScene" id=2]
@ -7,8 +7,10 @@
[ext_resource path="res://nodes/interaction_modes/interaction_mode.tscn" type="PackedScene" id=5] [ext_resource path="res://nodes/interaction_modes/interaction_mode.tscn" type="PackedScene" id=5]
[ext_resource path="res://nodes/ui/build_mode_ui.tscn" type="PackedScene" id=6] [ext_resource path="res://nodes/ui/build_mode_ui.tscn" type="PackedScene" id=6]
[ext_resource path="res://nodes/interaction_modes/build_mode.tscn" type="PackedScene" id=7] [ext_resource path="res://nodes/interaction_modes/build_mode.tscn" type="PackedScene" id=7]
[ext_resource path="res://resources/tile_types/wild.tres" type="Resource" id=8] [ext_resource path="res://nodes/ui/overlays UI.tscn" type="PackedScene" id=8]
[ext_resource path="res://nodes/ui/mode_selection_ui.tscn" type="PackedScene" id=9] [ext_resource path="res://nodes/ui/mode_selection_ui.tscn" type="PackedScene" id=9]
[ext_resource path="res://nodes/overlays.tscn" type="PackedScene" id=10]
[ext_resource path="res://resources/overlays/overlays.tres" type="Resource" id=11]
[sub_resource type="Curve" id=1] [sub_resource type="Curve" id=1]
_data = [ Vector2( 0, 0 ), 0.0, 5.0, 0, 0, Vector2( 0.5, 1 ), 0.0, 0.0, 0, 0, Vector2( 1, 0 ), -4.0, 0.0, 0, 0 ] _data = [ Vector2( 0, 0 ), 0.0, 5.0, 0, 0, Vector2( 0.5, 1 ), 0.0, 0.0, 0, 0, Vector2( 1, 0 ), -4.0, 0.0, 0, 0 ]
@ -18,21 +20,24 @@ _data = [ Vector2( 0, 0 ), 0.0, 5.0, 0, 0, Vector2( 0.5, 1 ), 0.0, 0.0, 0, 0, Ve
[node name="Clock" parent="." instance=ExtResource( 4 )] [node name="Clock" parent="." instance=ExtResource( 4 )]
_autoTick = true _autoTick = true
[node name="Grid" parent="." instance=ExtResource( 1 )] [node name="World Grid" parent="." instance=ExtResource( 1 )]
DiffusionCoefficient = 0.1 DiffusionCoefficient = 0.1
_startTileTypeResource = ExtResource( 8 )
[node name="Cursor" parent="." instance=ExtResource( 2 )] [node name="Cursor" parent="." instance=ExtResource( 2 )]
Grid = NodePath("../Grid") Grid = NodePath("../World Grid")
_pulseShape = SubResource( 1 ) _pulseShape = SubResource( 1 )
[node name="Interaction Modes" parent="." instance=ExtResource( 5 )] [node name="Interaction Modes" parent="." instance=ExtResource( 5 )]
_buildModePath = NodePath("Build Mode") _buildModePath = NodePath("Build Mode")
[node name="Build Mode" parent="Interaction Modes" instance=ExtResource( 7 )] [node name="Build Mode" parent="Interaction Modes" instance=ExtResource( 7 )]
_gridPath = NodePath("../../Grid") _gridPath = NodePath("../../World Grid")
_cursorPath = NodePath("../../Cursor") _cursorPath = NodePath("../../Cursor")
[node name="Overlays" parent="." instance=ExtResource( 10 )]
_overlaysResource = ExtResource( 11 )
_worldGridPath = NodePath("../World Grid")
[node name="UI" type="Control" parent="."] [node name="UI" type="Control" parent="."]
anchor_right = 1.0 anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
@ -45,15 +50,19 @@ _buildModePath = NodePath("../../Interaction Modes/Build Mode")
[node name="Mode Selection" parent="UI" instance=ExtResource( 9 )] [node name="Mode Selection" parent="UI" instance=ExtResource( 9 )]
[node name="Overlays UI" parent="UI" instance=ExtResource( 8 )]
_overlaysPath = NodePath("../../Overlays")
[connection signal="OnPauseChanged" from="Clock" to="UI/Debug" method="_on_Clock_OnPauseChanged"] [connection signal="OnPauseChanged" from="Clock" to="UI/Debug" method="_on_Clock_OnPauseChanged"]
[connection signal="OnTick" from="Clock" to="Grid" method="_on_Clock_OnTick"] [connection signal="OnTick" from="Clock" to="World Grid" method="_on_Clock_OnTick"]
[connection signal="OnTick" from="Clock" to="UI/Debug" method="_on_Clock_OnTick"] [connection signal="OnTick" from="Clock" to="UI/Debug" method="_on_Clock_OnTick"]
[connection signal="OnInteractionModeChanged" from="Interaction Modes" to="Cursor" method="_on_Interaction_Mode_OnInteractionModeChanged"] [connection signal="OnInteractionModeChanged" from="Interaction Modes" to="Cursor" method="_on_Interaction_Mode_OnInteractionModeChanged"]
[connection signal="OnInteractionModeChanged" from="Interaction Modes" to="UI/Debug" method="_on_Interaction_Mode_OnInteractionModeChanged"] [connection signal="OnInteractionModeChanged" from="Interaction Modes" to="UI/Debug" method="_on_Interaction_Mode_OnInteractionModeChanged"]
[connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeEntered"] [connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeEntered"]
[connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="UI/Mode Selection" method="EnableBuildMode"] [connection signal="OnModeEntered" from="Interaction Modes/Build Mode" to="UI/Mode Selection" method="EnableBuildMode"]
[connection signal="OnModeExited" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeExited"] [connection signal="OnModeExited" from="Interaction Modes/Build Mode" to="Interaction Modes" method="_on_Build_Mode_OnModeExited"]
[connection signal="OnModeExited" from="Interaction Modes/Build Mode" to="UI/Mode Selection" method="Enable"]
[connection signal="SelectedTileTypeChanged" from="Interaction Modes/Build Mode" to="UI/Build Mode" method="UpdateButtonToggleState"] [connection signal="SelectedTileTypeChanged" from="Interaction Modes/Build Mode" to="UI/Build Mode" method="UpdateButtonToggleState"]
[connection signal="OverlayCycled" from="Overlays" to="UI/Overlays UI" method="UpdateOverlayText"]
[connection signal="OnExit" from="UI/Build Mode" to="Interaction Modes" method="Reset"] [connection signal="OnExit" from="UI/Build Mode" to="Interaction Modes" method="Reset"]
[connection signal="OnExit" from="UI/Build Mode" to="UI/Mode Selection" method="Enable"]
[connection signal="BuildModeEnabled" from="UI/Mode Selection" to="Interaction Modes/Build Mode" method="Enable"] [connection signal="BuildModeEnabled" from="UI/Mode Selection" to="Interaction Modes/Build Mode" method="Enable"]

View File

@ -1,10 +1,11 @@
[gd_scene load_steps=3 format=2] [gd_scene load_steps=4 format=2]
[ext_resource path="res://scripts/WorldGrid.cs" type="Script" id=1] [ext_resource path="res://scripts/WorldGrid.cs" type="Script" id=1]
[ext_resource path="res://nodes/tile.tscn" type="PackedScene" id=2] [ext_resource path="res://nodes/tile.tscn" type="PackedScene" id=2]
[ext_resource path="res://resources/tiles/tiles.tres" type="Resource" id=3]
[node name="Grid" type="Node2D"] [node name="Grid" type="Node2D"]
script = ExtResource( 1 ) script = ExtResource( 1 )
TileScene = ExtResource( 2 ) TileScene = ExtResource( 2 )
Size = 10
CellSize = 64 CellSize = 64
_tileGridResource = ExtResource( 3 )

View File

@ -1,7 +1,7 @@
[gd_scene load_steps=3 format=2] [gd_scene load_steps=3 format=2]
[ext_resource path="res://scripts/interaction_modes/BuildMode.cs" type="Script" id=1] [ext_resource path="res://scripts/interaction_modes/BuildMode.cs" type="Script" id=1]
[ext_resource path="res://resources/tile_types/tile_type_collections/buildable_tiles.tres" type="Resource" id=2] [ext_resource path="res://resources/tiles/tile_types/tile_type_collections/buildable_tiles.tres" type="Resource" id=2]
[node name="Build Mode" type="Node"] [node name="Build Mode" type="Node"]
script = ExtResource( 1 ) script = ExtResource( 1 )

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://scripts/overlays/Overlays.cs" type="Script" id=1]
[ext_resource path="res://resources/overlays/heat_overlay.tres" type="Resource" id=2]
[ext_resource path="res://resources/overlays/no_overlay.tres" type="Resource" id=3]
[ext_resource path="res://resources/tiles/tiles.tres" type="Resource" id=4]
[node name="Overlays" type="Node"]
script = ExtResource( 1 )
_cycleOverlayKey = 16777218
_overlayResources = [ ExtResource( 3 ), ExtResource( 2 ) ]
_tileGridResource = ExtResource( 4 )

View File

@ -14,6 +14,6 @@ margin_left = 2.0
margin_top = 562.0 margin_top = 562.0
margin_right = 38.0 margin_right = 38.0
margin_bottom = 598.0 margin_bottom = 598.0
text = "Build Mode" text = "[B] Build Mode"
[connection signal="pressed" from="Build Mode" to="." method="EnableBuildMode"] [connection signal="pressed" from="Build Mode" to="." method="EnableBuildMode"]

View File

@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=2]
[ext_resource path="res://scripts/ui/OverlaysUI.cs" type="Script" id=1]
[node name="Overlays UI" type="Node"]
script = ExtResource( 1 )
_cycleOverlayButtonPath = NodePath("Button")
[node name="Button" type="Button" parent="."]
anchor_top = 1.0
anchor_bottom = 1.0
margin_top = -90.0
margin_right = 42.0
margin_bottom = -52.0
grow_vertical = 0
text = "[Tab] Cycle Overlay"
[connection signal="pressed" from="Button" to="." method="CycleOverlay"]

View File

@ -0,0 +1,8 @@
[gd_resource type="Resource" load_steps=2 format=2]
[ext_resource path="res://scripts/overlays/HeatOverlay.cs" type="Script" id=1]
[resource]
script = ExtResource( 1 )
_coldColor = Color( 0, 0, 0, 1 )
_hotColor = Color( 1, 0.654902, 0, 1 )

View File

@ -0,0 +1,6 @@
[gd_resource type="Resource" load_steps=2 format=2]
[ext_resource path="res://scripts/overlays/NoOverlay.cs" type="Script" id=1]
[resource]
script = ExtResource( 1 )

View File

@ -0,0 +1,9 @@
[gd_resource type="Resource" load_steps=4 format=2]
[ext_resource path="res://scripts/overlays/OverlayCollection.cs" type="Script" id=1]
[ext_resource path="res://resources/overlays/no_overlay.tres" type="Resource" id=2]
[ext_resource path="res://resources/overlays/heat_overlay.tres" type="Resource" id=3]
[resource]
script = ExtResource( 1 )
_resources = [ ExtResource( 2 ), ExtResource( 3 ) ]

View File

@ -1,8 +0,0 @@
[gd_resource type="Resource" load_steps=3 format=2]
[ext_resource path="res://scripts/TileTypeCollection.cs" type="Script" id=1]
[ext_resource path="res://resources/tile_types/developed.tres" type="Resource" id=2]
[resource]
script = ExtResource( 1 )
_tileTypeResources = [ ExtResource( 2 ) ]

View File

@ -1,10 +1,10 @@
[gd_resource type="Resource" load_steps=2 format=2] [gd_resource type="Resource" load_steps=2 format=2]
[ext_resource path="res://scripts/TileType.cs" type="Script" id=1] [ext_resource path="res://scripts/tile/TileType.cs" type="Script" id=1]
[resource] [resource]
script = ExtResource( 1 ) script = ExtResource( 1 )
Name = "Developed" Name = "Developed"
BuildLabel = "[D]eveloped"
Key = 68 Key = 68
Color = Color( 0.372549, 0.372549, 0.372549, 1 ) Color = Color( 0.372549, 0.372549, 0.372549, 1 )
HeatGeneration = 0.4

View File

@ -0,0 +1,8 @@
[gd_resource type="Resource" load_steps=3 format=2]
[ext_resource path="res://scripts/tile/TileTypeCollection.cs" type="Script" id=1]
[ext_resource path="res://resources/tiles/tile_types/developed.tres" type="Resource" id=2]
[resource]
script = ExtResource( 1 )
_resources = [ ExtResource( 2 ) ]

View File

@ -1,10 +1,10 @@
[gd_resource type="Resource" load_steps=2 format=2] [gd_resource type="Resource" load_steps=2 format=2]
[ext_resource path="res://scripts/TileType.cs" type="Script" id=1] [ext_resource path="res://scripts/tile/TileType.cs" type="Script" id=1]
[resource] [resource]
script = ExtResource( 1 ) script = ExtResource( 1 )
Name = "Wild" Name = "Wild"
BuildLabel = "[W]ild"
Key = 87 Key = 87
Color = Color( 0, 0.545098, 0.0196078, 1 ) Color = Color( 0, 0.545098, 0.0196078, 1 )
HeatGeneration = -0.2

View File

@ -0,0 +1,9 @@
[gd_resource type="Resource" load_steps=3 format=2]
[ext_resource path="res://scripts/tile/TileGrid.cs" type="Script" id=1]
[ext_resource path="res://resources/tiles/tile_types/wild.tres" type="Resource" id=2]
[resource]
script = ExtResource( 1 )
Size = 10
_startTileTypeResource = ExtResource( 2 )

View File

@ -0,0 +1,33 @@
using Godot;
using System.Collections;
using System.Collections.Generic;
public abstract class ReadOnlyResourceList<T> : Resource, IReadOnlyList<T> where T : Resource
{
[Export]
private List<Resource> _resources = new List<Resource>();
private List<T> Collection
{
get
{
if (_collection != null)
return _collection;
_collection = new List<T>();
foreach (var resource in _resources)
{
_collection.Add((T)resource);
}
return _collection;
}
}
private List<T> _collection = null;
public T this[int index] => Collection[index];
public int Count => Collection.Count;
public IEnumerator<T> GetEnumerator() => Collection.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator();
}

View File

@ -1,46 +0,0 @@
using Godot;
using System;
using System.Collections;
using System.Collections.Generic;
public class TileTypeCollection : Resource, IReadOnlyList<TileType>
{
[Export]
private List<Resource> _tileTypeResources = new List<Resource>();
private List<TileType> TileTypes
{
get
{
if (_tileTypes != null)
return _tileTypes;
_tileTypes = new List<TileType>();
foreach (var resource in _tileTypeResources)
{
if (!(resource is TileType))
throw new InvalidCastException($"{resource} must be a {typeof(TileType)}");
_tileTypes.Add((TileType)resource);
}
return _tileTypes;
}
}
private List<TileType> _tileTypes = null;
public TileType this[int index] => TileTypes[index];
public int Count => TileTypes.Count;
public IEnumerator<TileType> GetEnumerator()
{
return TileTypes.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return TileTypes.GetEnumerator();
}
}

View File

@ -7,11 +7,8 @@ public class WorldGrid : Node2D
private PackedScene TileScene { get; set; } private PackedScene TileScene { get; set; }
[Export] [Export]
private Resource _startTileTypeResource; private Resource _tileGridResource;
private TileType _startTileType; private TileGrid _tileGrid;
[Export]
public int Size { get; set; }
[Export] [Export]
public int CellSize { get; set; } public int CellSize { get; set; }
@ -19,65 +16,50 @@ public class WorldGrid : Node2D
[Export] [Export]
public float DiffusionCoefficient { get; set; } = 0.01f; public float DiffusionCoefficient { get; set; } = 0.01f;
private Tile[,] _tiles; private float[] _nextValues = null;
private float[,] _nextValues = null;
public struct TileView
{
public ShaderMaterial material;
}
private TileView[] _tileViews;
// 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()
{ {
_startTileType = (TileType)_startTileTypeResource; _tileGrid = (TileGrid)_tileGridResource;
GenerateGrid(Size); GenerateCanvasItems(_tileGrid);
}
public override void _Process(float delta)
{
base._Process(delta);
for (int x = 0; x < Size; x++)
{
for (int y = 0; y < Size; y++)
{
var tile = _tiles[x, y];
var material = tile.material;
material.SetShaderParam("t", tile.value);
}
}
}
public bool IsInBounds(int x, int y)
{
return x >= 0 && x < Size && y >= 0 && y < Size;
}
public void ToggleTileHighlight(int x, int y)
{
if (!IsInBounds(x, y))
return;
_tiles[x, y].isHighlighted ^= true;
}
public void SetTileType(int x, int y, TileType tileType)
{
if (!IsInBounds(x, y))
return;
GD.Print($"set ({x}, {y}) to {tileType}");
_tiles[x, y].type = tileType;
_tiles[x, y].material.SetShaderParam("lowColor", tileType.Color);
} }
#region Positioning
public void GetGridPos(Vector2 position, out int x, out int y) public void GetGridPos(Vector2 position, out int x, out int y)
{ {
x = Mathf.FloorToInt(position.x / CellSize); x = Mathf.FloorToInt(position.x / CellSize);
y = Mathf.FloorToInt(position.y / CellSize); y = Mathf.FloorToInt(position.y / CellSize);
} }
public bool IsInBounds(int x, int y) => _tileGrid.IsInBounds(x, y);
#endregion
#region Interaction
public void SetTileType(int x, int y, TileType tileType)
{
if (!_tileGrid.IsInBounds(x, y))
return;
_tileGrid.MapPosition(x, y, out var idx);
var tile = _tileGrid[idx];
tile.type = tileType;
_tileGrid[x, y] = tile;
_tileViews[idx].material.SetShaderParam("lowColor", tileType.Color);
}
#endregion
private void Clear() private void Clear()
{ {
_tiles = null;
int children = this.GetChildCount(); int children = this.GetChildCount();
GD.Print(children); GD.Print(children);
for (int i = 0; i < children; i++) for (int i = 0; i < children; i++)
@ -85,43 +67,46 @@ public class WorldGrid : Node2D
var child = this.GetChild(i); var child = this.GetChild(i);
child.QueueFree(); child.QueueFree();
} }
_tileViews = null;
} }
private void GenerateGrid(int size) private void GenerateCanvasItems(TileGrid grid)
{ {
this.Clear(); this.Clear();
_tiles = new Tile[size, size]; _tileViews = new TileView[grid.Count];
_nextValues = new float[size, size]; _nextValues = new float[grid.Count];
for (int x = 0; x < size; x++) for (int i = 0; i < grid.Count; i++)
{ {
for (int y = 0; y < size; y++) _tileGrid.MapIndex(i, out var x, out var y);
var node = TileScene.Instance<Node2D>();
this.AddChild(node);
var position = new Vector2
{ {
var tile = new Tile(); x = (x + .5f) * CellSize,
tile.isHighlighted = false; y = (y + .5f) * CellSize
tile.value = 0.0f; };
tile.type = _startTileType; node.Position = position;
var canvasItem = (CanvasItem)node;
var node = TileScene.Instance<Node2D>(); var tile = _tileGrid[i];
this.AddChild(node);
var position = new Vector2
{
x = (x + .5f) * CellSize,
y = (y + .5f) * CellSize
};
node.Position = position;
var canvasItem = (CanvasItem)node;
tile.material = (ShaderMaterial)canvasItem.Material;
tile.material.SetShaderParam("lowColor", _startTileType.Color);
_tiles[x, y] = tile; TileView view = default;
} view.material = (ShaderMaterial)canvasItem.Material;
view.material.SetShaderParam("lowColor", _tileGrid.StartTileType.Color);
_tileViews[i] = view;
} }
} }
public ShaderMaterial GetTileMaterial(int idx) => _tileViews[idx].material;
#region Simulation
public void _on_Clock_OnTick(int ticks) public void _on_Clock_OnTick(int ticks)
{ {
GenerateHeat();
Diffuse(); Diffuse();
} }
@ -135,42 +120,56 @@ public class WorldGrid : Node2D
private void GetNeighbourTemperatures(int x, int y, out float nx, out float ny, out float px, out float py) private void GetNeighbourTemperatures(int x, int y, out float nx, out float ny, out float px, out float py)
{ {
// default value // default value
var t = _tiles[x, y].value; var t = _tileGrid[x, y].temperature;
nx = x > 0 ? _tiles[x - 1, y].value : t; nx = x > 0 ? _tileGrid[x - 1, y].temperature : t;
px = x < Size - 1 ? _tiles[x + 1, y].value : t; px = x < _tileGrid.Size - 1 ? _tileGrid[x + 1, y].temperature : t;
ny = y > 0 ? _tiles[x, y - 1].value : t; ny = y > 0 ? _tileGrid[x, y - 1].temperature : t;
py = y < Size - 1 ? _tiles[x, y + 1].value : t; py = y < _tileGrid.Size - 1 ? _tileGrid[x, y + 1].temperature : t;
}
private void GenerateHeat()
{
for (int i = 0; i < _tileGrid.Count; i++)
{
var tile = _tileGrid[i];
var type = tile.type;
tile.temperature += type.HeatGeneration;
_tileGrid[i] = tile;
}
} }
private void Diffuse() private void Diffuse()
{ {
for (int x = 0; x < Size; x++) var D = DiffusionCoefficient;
for (int i = 0; i < _tileGrid.Count; i++)
{ {
for (int y = 0; y < Size; y++) float t = _tileGrid[i].temperature;
{
float t = _tiles[x, y].value;
var D = DiffusionCoefficient;
GetNeighbourTemperatures( _tileGrid.MapIndex(i, out var x, out var y);
x, y,
out var nx,
out var ny,
out var px,
out var py);
// current value GetNeighbourTemperatures(
_nextValues[x, y] = TransferHeat(t, D, nx, ny, px, py); x, y,
} out var nx,
out var ny,
out var px,
out var py);
var temperature = TransferHeat(t, D, nx, ny, px, py);
// TODO: what if it's really really cold out?
temperature = Mathf.Max(0, temperature);
_nextValues[i] = temperature;
} }
for (int x = 0; x < Size; x++) for (int i = 0; i < _tileGrid.Count; i++)
{ {
for (int y = 0; y < Size; y++) var tile = _tileGrid[i];
{ tile.temperature = _nextValues[i];
_tiles[x, y].value = _nextValues[x, y]; _tileGrid[i] = tile;
}
} }
} }
#endregion
} }

View File

@ -0,0 +1,18 @@
using Godot;
using System;
public class HeatOverlay : Overlay
{
[Export]
private Color _coldColor;
[Export]
private Color _hotColor;
public override void Apply(Tile tile, ShaderMaterial material)
{
material.SetShaderParam("lowColor", _coldColor);
material.SetShaderParam("highColor", _hotColor);
material.SetShaderParam("t", tile.temperature);
}
}

View File

@ -0,0 +1,12 @@
using Godot;
public class NoOverlay : Overlay
{
// in this view we want to draw tiles with their normal colour
public override void Apply(Tile tile, ShaderMaterial material)
{
var type = tile.type;
material.SetShaderParam("lowColor", type.Color);
material.SetShaderParam("t", 0);
}
}

View File

@ -0,0 +1,7 @@
using Godot;
public abstract class Overlay : Resource
{
public string Name => GetType().ToString();
public abstract void Apply(Tile tile, ShaderMaterial material);
}

View File

@ -0,0 +1,3 @@
public class OverlayCollection : ReadOnlyResourceList<Overlay>
{
}

View File

@ -0,0 +1,67 @@
using Godot;
using System;
public class Overlays : Node
{
[Signal]
delegate void OverlayCycled(Overlay overlay);
[Export]
private KeyList _cycleOverlayKey;
[Export] private Resource _overlaysResource;
public Overlay Current => _overlays[_overlayIdx];
private OverlayCollection _overlays;
private int _overlayIdx = 0;
[Export]
private Resource _tileGridResource;
private TileGrid _tileGrid;
[Export]
private NodePath _worldGridPath;
private WorldGrid _worldGrid;
public override void _Ready()
{
_overlays = (OverlayCollection)_overlaysResource;
_tileGrid = (TileGrid)_tileGridResource;
_worldGrid = GetNode<WorldGrid>(_worldGridPath);
}
public override void _Input(InputEvent @event)
{
base._Input(@event);
if (!(@event is InputEventKey keyEvent))
return;
if (!(keyEvent.Pressed))
return;
if ((KeyList)keyEvent.Scancode != _cycleOverlayKey)
return;
CycleOverlay();
}
public override void _Process(float delta)
{
base._Process(delta);
for (int i = 0; i < _tileGrid.Count; i++)
{
var tile = _tileGrid[i];
var material = _worldGrid.GetTileMaterial(i);
Current.Apply(tile, material);
}
}
public void CycleOverlay()
{
_overlayIdx++;
_overlayIdx %= _overlays.Count;
EmitSignal(nameof(OverlayCycled), Current);
}
}

View File

@ -3,7 +3,6 @@ using Godot;
public struct Tile public struct Tile
{ {
public bool isHighlighted; public bool isHighlighted;
public float value; public float temperature;
public ShaderMaterial material;
public TileType type; public TileType type;
} }

View File

@ -0,0 +1,92 @@
using Godot;
using System;
using System.Collections;
using System.Collections.Generic;
public class TileGrid : Resource, IReadOnlyList<Tile>
{
[Export]
public int Size { get; private set; } = 10;
[Export]
private Resource _startTileTypeResource;
public TileType StartTileType => (TileType)_startTileTypeResource;
public int Count => Size * Size;
public Tile this[int index]
{
get => Tiles[index];
set => Tiles[index] = value;
}
public Tile this[int x, int y]
{
get
{
MapPosition(x, y, out int idx);
return Tiles[idx];
}
set
{
MapPosition(x, y, out int idx);
Tiles[idx] = value;
}
}
private List<Tile> Tiles
{
get
{
if (_tiles == null)
{
_tiles = new List<Tile>();
Generate(_tiles);
}
return _tiles;
}
}
private List<Tile> _tiles = null;
private void Generate(List<Tile> tiles)
{
tiles.Clear();
for (int i = 0; i < Count; i++)
{
var tile = new Tile();
tile.isHighlighted = false;
tile.temperature = 0.0f;
tile.type = StartTileType;
tiles.Add(tile);
}
}
public IEnumerator<Tile> GetEnumerator()
{
return Tiles.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return Tiles.GetEnumerator();
}
public void MapIndex(int idx, out int x, out int y)
{
y = idx / Size;
x = idx % Size;
}
public void MapPosition(int x, int y, out int idx)
{
idx = y * Size + x;
}
public bool IsInBounds(int x, int y)
{
return x >= 0 && x < Size && y >= 0 && y < Size;
}
}

View File

@ -5,8 +5,7 @@ public class TileType : Resource
[Export] [Export]
public string Name { get; private set; } public string Name { get; private set; }
[Export] public string BuildLabel => $"[{Key}] {Name}";
public string BuildLabel { get; private set; }
[Export] [Export]
public KeyList Key { get; private set; } public KeyList Key { get; private set; }
@ -14,8 +13,8 @@ public class TileType : Resource
[Export] [Export]
public Color Color { get; private set; } public Color Color { get; private set; }
public override string ToString() [Export]
{ public float HeatGeneration { get; private set; }
return Name;
} public override string ToString() => Name;
} }

View File

@ -0,0 +1,3 @@
public class TileTypeCollection : ReadOnlyResourceList<TileType>
{
}

View File

@ -0,0 +1,36 @@
using Godot;
using System;
using System.Collections.Generic;
public class OverlaysUI : Node
{
[Export]
private NodePath _cycleOverlayButtonPath;
private Button _cycleOverlayButton;
[Export]
private NodePath _overlaysPath;
private Overlays _overlays;
private string _mainText;
public override void _Ready()
{
base._Ready();
_cycleOverlayButton = GetNode<Button>(_cycleOverlayButtonPath);
_mainText = _cycleOverlayButton.Text;
_overlays = GetNode<Overlays>(_overlaysPath);
}
public void CycleOverlay()
{
_overlays.CycleOverlay();
}
public void UpdateOverlayText(Overlay overlay)
{
_cycleOverlayButton.Text = $"{_mainText} ({_overlays.Current.Name})";
}
}