|
@ -17,9 +17,9 @@ enum CellStates { |
|
|
@onready var world_seed_label: Label = $CanvasLayer/Debug/VBoxContainer/WorldSeed |
|
|
@onready var world_seed_label: Label = $CanvasLayer/Debug/VBoxContainer/WorldSeed |
|
|
|
|
|
|
|
|
@export var world_seed: int |
|
|
@export var world_seed: int |
|
|
@export var cell_size: Vector2 = Vector2(64, 64) |
|
|
|
|
|
|
|
|
@export var cell_size: Vector2 = Vector2(16, 16) |
|
|
@export var cell_texture: Texture2D |
|
|
@export var cell_texture: Texture2D |
|
|
@export var world_size: Vector2 = Vector2(8, 8) |
|
|
|
|
|
|
|
|
@export var world_size: Vector2 = Vector2(32, 32) |
|
|
|
|
|
|
|
|
var cell_instance |
|
|
var cell_instance |
|
|
var generation: int = 1 |
|
|
var generation: int = 1 |
|
@ -37,29 +37,26 @@ func _ready() -> void: |
|
|
generate_world() |
|
|
generate_world() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Check a cell against the Conway rules |
|
|
|
|
|
func check_cell_rules(pos: Vector2) -> void: |
|
|
|
|
|
|
|
|
## Check a cell against the Conway rules and return True if cell shoudl be alive |
|
|
|
|
|
func cell_is_alive(pos: Vector2) -> bool: |
|
|
var neighbors := count_living_neighbors(pos) |
|
|
var neighbors := count_living_neighbors(pos) |
|
|
var currently_alive = world[pos.x][pos.y] is RID |
|
|
var currently_alive = world[pos.x][pos.y] is RID |
|
|
|
|
|
|
|
|
# Rule 1 - Any live cell with fewer than two live neighbours dies (underpopulation) |
|
|
# Rule 1 - Any live cell with fewer than two live neighbours dies (underpopulation) |
|
|
if currently_alive and neighbors < 2: |
|
|
if currently_alive and neighbors < 2: |
|
|
kill_cell(pos) |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
return false |
|
|
# Rule 2 - Any live cell with two or three live neighbours lives |
|
|
# Rule 2 - Any live cell with two or three live neighbours lives |
|
|
elif currently_alive and (neighbors == 2 or neighbors == 3): |
|
|
elif currently_alive and (neighbors == 2 or neighbors == 3): |
|
|
total_living += 1 |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
return true |
|
|
# Rule 3 - Any live cell with more than three live neighbours dies (overpopulation) |
|
|
# Rule 3 - Any live cell with more than three live neighbours dies (overpopulation) |
|
|
elif currently_alive and neighbors > 3: |
|
|
|
|
|
kill_cell(pos) |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
return false |
|
|
# Rule 4 - Any dead cell with epos.xactly three live neighbours becomes a live cell (reproduction) |
|
|
# Rule 4 - Any dead cell with epos.xactly three live neighbours becomes a live cell (reproduction) |
|
|
elif not currently_alive and neighbors == 3: |
|
|
elif not currently_alive and neighbors == 3: |
|
|
total_living += 1 |
|
|
|
|
|
world[pos.x][pos.y] = create_cell(pos) |
|
|
|
|
|
return |
|
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
|
|
|
|
return false |
|
|
|
|
|
|
|
|
|
|
|
## Count all neighbors surrounding a cell |
|
|
func count_living_neighbors(pos: Vector2) -> int: |
|
|
func count_living_neighbors(pos: Vector2) -> int: |
|
|
var count := 0 |
|
|
var count := 0 |
|
|
|
|
|
|
|
@ -75,18 +72,31 @@ func count_living_neighbors(pos: Vector2) -> int: |
|
|
count += 1 |
|
|
count += 1 |
|
|
return count |
|
|
return count |
|
|
|
|
|
|
|
|
|
|
|
## Loop through the world and create or kill cells depending on rules |
|
|
func process_generation() -> void: |
|
|
func process_generation() -> void: |
|
|
generation += 1 |
|
|
generation += 1 |
|
|
total_living = 0 |
|
|
total_living = 0 |
|
|
|
|
|
|
|
|
|
|
|
var new_world: Array= [] |
|
|
for x in range(world_size.x): |
|
|
for x in range(world_size.x): |
|
|
|
|
|
new_world.append([]) |
|
|
|
|
|
new_world[x].resize(world_size.x) |
|
|
for y in range(world_size.y): |
|
|
for y in range(world_size.y): |
|
|
check_cell_rules(Vector2(x, y)) |
|
|
|
|
|
|
|
|
var pos = Vector2(x, y) |
|
|
|
|
|
if cell_is_alive(pos): |
|
|
|
|
|
total_living += 1 |
|
|
|
|
|
new_world[x][y] = create_cell(pos) |
|
|
|
|
|
else: |
|
|
|
|
|
new_world[x][y] = kill_cell(pos) |
|
|
|
|
|
|
|
|
|
|
|
world = new_world |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Create the cell using the rendering server |
|
|
## Create the cell using the rendering server |
|
|
func create_cell(pos: Vector2) -> RID: |
|
|
func create_cell(pos: Vector2) -> RID: |
|
|
|
|
|
var world_size = world.size() |
|
|
|
|
|
if world[pos.x][pos.y] is RID: return world[pos.x][pos.y] |
|
|
|
|
|
|
|
|
var rs = RenderingServer |
|
|
var rs = RenderingServer |
|
|
|
|
|
|
|
|
cell_instance = rs.canvas_item_create() |
|
|
cell_instance = rs.canvas_item_create() |
|
@ -101,23 +111,23 @@ func create_cell(pos: Vector2) -> RID: |
|
|
|
|
|
|
|
|
return cell_instance |
|
|
return cell_instance |
|
|
|
|
|
|
|
|
func kill_cell(pos: Vector2) -> void: |
|
|
|
|
|
if not world[pos.x][pos.y] is RID: return |
|
|
|
|
|
|
|
|
|
|
|
RenderingServer.free_rid(world[pos.x][pos.y]) |
|
|
|
|
|
world[pos.x][pos.y] = CellStates.DEAD |
|
|
|
|
|
|
|
|
func kill_cell(pos: Vector2) -> CellStates: |
|
|
|
|
|
if world[pos.x][pos.y] is RID: |
|
|
|
|
|
RenderingServer.free_rid(world[pos.x][pos.y]) |
|
|
|
|
|
return CellStates.DEAD |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Generate the world with the initial cells |
|
|
## Generate the world with the initial cells |
|
|
func generate_world() -> void: |
|
|
func generate_world() -> void: |
|
|
for x in range(world_size.x): |
|
|
for x in range(world_size.x): |
|
|
world.append([]) |
|
|
world.append([]) |
|
|
|
|
|
world[x].resize(world_size.x) |
|
|
for y in range(world_size.y): |
|
|
for y in range(world_size.y): |
|
|
if randi_range(0, 1): |
|
|
if randi_range(0, 1): |
|
|
total_living += 1 |
|
|
total_living += 1 |
|
|
world[x].append(create_cell(Vector2(x, y))) |
|
|
|
|
|
|
|
|
world[x][y] = create_cell(Vector2(x, y)) |
|
|
else: |
|
|
else: |
|
|
world[x].append(CellStates.DEAD) |
|
|
|
|
|
|
|
|
world[x][y] = CellStates.DEAD |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _on_generation_timer_timeout() -> void: |
|
|
func _on_generation_timer_timeout() -> void: |
|
|