Player logic and pathfinding
parent
208dfd20d5
commit
567b2cb689
|
|
@ -1,17 +1,11 @@
|
|||
- Sprites (dome)
|
||||
- Initialize Tilemap (Yan)
|
||||
- Script --> World (manages access to tilemap)
|
||||
- Player is on tilemap
|
||||
- Multiple layers
|
||||
- Ground (Water, Ground) --> custom data (Walkable, Weight = 1)
|
||||
- Obstacles (Stone, ...)
|
||||
- Pickup (Berries, Trees, Boat parts) --> custom data (Type of collectable "tree", "berry", "boat")
|
||||
- Temperature layer (normal and different levels of coldness, transparent solid color)
|
||||
## Todo
|
||||
|
||||
- Sprites (Dome)
|
||||
- Design a tilemap for the game
|
||||
- Player (Dome)
|
||||
- Stats (see document)
|
||||
- As a layer in the tilemap
|
||||
- Implemented in the PlayerManager
|
||||
- Stats (see document)
|
||||
- Inventory
|
||||
- with one slot
|
||||
- can pick up items from tilemap (Pickup)
|
||||
|
|
@ -20,10 +14,6 @@
|
|||
- Use tilemap layers to compute route
|
||||
- Support obstacles
|
||||
- Result must be an array of tile coordinates, length (array length) and the total cost (sum of weights)
|
||||
- Decision Tree (Classes, etc.) (Yan)
|
||||
- Reference Food Gatherer for implementation
|
||||
- Child of player, serves as controller
|
||||
- Script needs access to the scene, player and other objects/data
|
||||
- Implement Behaviours
|
||||
- Implement all kinds of Behaviours, see document
|
||||
- Visualization, make the simulation understandable
|
||||
|
|
@ -31,3 +21,18 @@
|
|||
- Distances
|
||||
- Current navigation path
|
||||
- ...
|
||||
|
||||
## Done
|
||||
|
||||
- Initialize Tilemap (Yan)
|
||||
- Script --> World (manages access to tilemap)
|
||||
- Player is on tilemap
|
||||
- Multiple layers
|
||||
- Ground (Water, Ground) --> custom data (Walkable, Weight = 1)
|
||||
- Obstacles (Stone, ...)
|
||||
- Pickup (Berries, Trees, Boat parts) --> custom data (Type of collectable "tree", "berry", "boat")
|
||||
- Temperature layer (normal and different levels of coldness, transparent solid color)
|
||||
- Decision Tree (Classes, etc.) (Yan)
|
||||
- Reference Food Gatherer for implementation
|
||||
- Child of player, serves as controller
|
||||
- Script needs access to the scene, player and other objects/data
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,48 +1 @@
|
|||
{
|
||||
"filename": "tilemaps.aseprite",
|
||||
"height": 320,
|
||||
"width": 320,
|
||||
"layers": [
|
||||
{
|
||||
"name": "ground",
|
||||
"cels": [
|
||||
{
|
||||
"image": "tilemaps\\tilemap_ground.png",
|
||||
"frame": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "objects",
|
||||
"cels": [
|
||||
{
|
||||
"image": "tilemaps\\tilemap_objects.png",
|
||||
"frame": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "temperature",
|
||||
"cels": [
|
||||
{
|
||||
"image": "tilemaps\\tilemap_temperature.png",
|
||||
"frame": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "player",
|
||||
"cels": [
|
||||
{
|
||||
"image": "tilemaps\\tilemap_player.png",
|
||||
"frame": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"frames": [
|
||||
{
|
||||
"duration": 0.1
|
||||
}
|
||||
]
|
||||
}
|
||||
{"width":320,"layers":[{"cels":[{"image":"tilemaps\\tilemap_ground.png","frame":0}],"name":"ground"},{"cels":[{"image":"tilemaps\\tilemap_objects.png","frame":0}],"name":"objects"},{"cels":[{"image":"tilemaps\\tilemap_temperature.png","frame":0}],"name":"temperature"},{"cels":[{"image":"tilemaps\\tilemap_player.png","frame":0}],"name":"player"}],"frames":[{"duration":0.1}],"height":320,"filename":"tilemaps.aseprite"}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 433 B |
|
|
@ -9,22 +9,27 @@
|
|||
texture = ExtResource("1_ukrsa")
|
||||
0:0/0 = 0
|
||||
0:0/0/custom_data_0 = true
|
||||
0:0/0/custom_data_2 = 1
|
||||
1:0/0 = 0
|
||||
1:0/0/custom_data_2 = 1
|
||||
2:0/0 = 0
|
||||
2:0/0/custom_data_2 = 1
|
||||
3:0/0 = 0
|
||||
3:0/0/custom_data_0 = true
|
||||
3:0/0/custom_data_2 = 1
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_7jeam"]
|
||||
texture = ExtResource("2_o4fdg")
|
||||
0:0/0 = 0
|
||||
1:0/0 = 0
|
||||
2:0/0 = 0
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_1og8x"]
|
||||
texture = ExtResource("3_xap0v")
|
||||
0:0/0 = 0
|
||||
0:0/0/custom_data_1 = 100
|
||||
0:0/0/custom_data_1 = 10
|
||||
1:0/0 = 0
|
||||
1:0/0/custom_data_1 = -200
|
||||
1:0/0/custom_data_1 = 20
|
||||
|
||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_i41cv"]
|
||||
texture = ExtResource("4_f38wc")
|
||||
|
|
@ -35,6 +40,8 @@ custom_data_layer_0/name = "walkable"
|
|||
custom_data_layer_0/type = 1
|
||||
custom_data_layer_1/name = "temperature"
|
||||
custom_data_layer_1/type = 2
|
||||
custom_data_layer_2/name = "cost"
|
||||
custom_data_layer_2/type = 2
|
||||
sources/0 = SubResource("TileSetAtlasSource_114re")
|
||||
sources/1 = SubResource("TileSetAtlasSource_7jeam")
|
||||
sources/2 = SubResource("TileSetAtlasSource_1og8x")
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ tile_map_data = PackedByteArray("AAAAAAAAAAACAAAAAAABAAAAAAACAAAAAAACAAEAAAABAAA
|
|||
tile_set = ExtResource("1_vlccq")
|
||||
|
||||
[node name="NonInteractiveObjectsLayer" type="TileMapLayer" parent="Tileset"]
|
||||
tile_map_data = PackedByteArray("AAAOAAkAAQACAAAAAAANAAkAAQACAAAAAAANAAoAAQACAAAAAAAMAAoAAQACAAAAAAAMAAsAAQACAAAAAAAMAAwAAQACAAAAAAAMAA0AAQACAAAAAAALAA0AAQACAAAAAAALAA4AAQACAAAAAAAKAA4AAQACAAAAAAAKAA8AAQACAAAAAAAKABAAAQACAAAAAAAJABAAAQACAAAAAAAJABEAAQACAAAAAAAJABIAAQACAAAAAAAJABMAAQACAAAAAAAJABQAAQACAAAAAAAIABQAAQACAAAAAAAIABMAAQACAAAAAAAIABIAAQACAAAAAAAIABEAAQACAAAAAAAIABAAAQACAAAAAAAIAA8AAQACAAAAAAAJAA8AAQACAAAAAAAJAA4AAQACAAAAAAAKAA0AAQACAAAAAAAKAAwAAQACAAAAAAAKAAsAAQACAAAAAAALAAsAAQACAAAAAAALAAwAAQACAAAAAAALAAoAAQACAAAAAAAMAAkAAQACAAAAAAANAAgAAQACAAAAAAAOAAgAAQACAAAAAAA=")
|
||||
tile_set = ExtResource("1_vlccq")
|
||||
|
||||
[node name="InteractiveObjectsLayer" type="TileMapLayer" parent="Tileset"]
|
||||
|
|
@ -33,6 +34,7 @@ tile_set = ExtResource("1_vlccq")
|
|||
tile_set = ExtResource("1_vlccq")
|
||||
|
||||
[node name="TemperatureLayer" type="TileMapLayer" parent="Tileset"]
|
||||
tile_map_data = PackedByteArray("AAAAAAAAAgABAAAAAAA=")
|
||||
tile_set = ExtResource("1_vlccq")
|
||||
|
||||
[node name="PlayerManager" type="Node" parent="."]
|
||||
|
|
|
|||
|
|
@ -17,3 +17,7 @@ func _process(delta: float) -> void:
|
|||
camera.print_config()
|
||||
if Input.is_action_just_pressed("force_game_tick"):
|
||||
player.game_tick()
|
||||
|
||||
func player_health_depleted():
|
||||
# TODO
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,13 +1,24 @@
|
|||
class_name PlayerManager
|
||||
extends Node
|
||||
|
||||
@export var food_damage: int = 1
|
||||
@export var temperature_damage: int = 1
|
||||
@export var temperature_endure: int = 50
|
||||
|
||||
var tilemap_types: TileMapTileTypes = TileMapTileTypes.new()
|
||||
#
|
||||
var game_manager: GameManager = null
|
||||
var board_position: Vector2 = Vector2(0, 0)
|
||||
var board_position: Vector2i = Vector2i(8, 10)
|
||||
|
||||
@onready var behavior_tree: BehaviorTree = $BehaviorTree
|
||||
|
||||
var food: int = 0
|
||||
# var water: int = 0
|
||||
var temperature_timer: int = 0
|
||||
var health: int = 0
|
||||
#
|
||||
var inventory_slot: Vector2i = tilemap_types.EMPTY
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
call_deferred("defer_ready")
|
||||
|
|
@ -20,13 +31,96 @@ func defer_ready() -> void:
|
|||
|
||||
func _process(delta: float) -> void:
|
||||
if Input.is_action_just_pressed("key_3"):
|
||||
game_manager.camera.go_to_zooming(game_manager.world.tilemap_player.cell_to_local(board_position), 2)
|
||||
# game_manager.camera.go_to_zooming(game_manager.world.tilemap_player.cell_to_local(board_position), 2)
|
||||
for pos in game_manager.world.find_path(board_position, Vector2i(13, 15)):
|
||||
game_manager.world.tilemap_temperature.set_cell(pos, tilemap_types.TEMPERATURE_COLD_1)
|
||||
|
||||
|
||||
# SECTION: board access/mangement
|
||||
|
||||
func update_board() -> void:
|
||||
game_manager.world.tilemap_player.clear_cells()
|
||||
game_manager.world.tilemap_player.set_cell(board_position, tilemap_types.PLAYER)
|
||||
|
||||
|
||||
# SECTION: inventory system
|
||||
|
||||
func pick_up_item(tilemap_pos: Vector2i) -> void:
|
||||
var pick_up_cell: TileData = game_manager.world.tilemap_interactive.get_cell(tilemap_pos)
|
||||
if not pick_up_cell:
|
||||
push_warning("Player trying to pick up item that does not exist at ", tilemap_pos)
|
||||
return
|
||||
|
||||
var pick_up_item_type: Vector2i = game_manager.world.tilemap_interactive.tilemap.get_cell_atlas_coords(tilemap_pos)
|
||||
|
||||
if inventory_slot != tilemap_types.EMPTY:
|
||||
# set the type of the item on the tilemap to the one in the inventory, switching them
|
||||
game_manager.world.tilemap_interactive.set_cell(tilemap_pos, inventory_slot)
|
||||
|
||||
inventory_slot = pick_up_item_type
|
||||
|
||||
|
||||
# SECTION: player movement
|
||||
|
||||
func walk_towards(position: Vector2i) -> void:
|
||||
var path: Array[Vector2i] = game_manager.world.find_path(board_position, position)
|
||||
if len(path) > 1:
|
||||
var next_position: Vector2i = path[1]
|
||||
var direction: Vector2i = find_direction(board_position, next_position)
|
||||
move_player(direction)
|
||||
|
||||
|
||||
func move_player(direction: Vector2i) -> void:
|
||||
var new_position: Vector2 = board_position + direction
|
||||
if game_manager.world.is_walkable(new_position):
|
||||
board_position = new_position
|
||||
else:
|
||||
push_warning("Player trying to move to non-walkable position, prevented ", new_position)
|
||||
|
||||
|
||||
func find_direction(pos_a: Vector2i, pos_b: Vector2i) -> Vector2i:
|
||||
var direction: Vector2i = Vector2i(0, 0)
|
||||
if pos_a.x < pos_b.x:
|
||||
direction.x = 1
|
||||
elif pos_a.x > pos_b.x:
|
||||
direction.x = -1
|
||||
|
||||
if pos_a.y < pos_b.y:
|
||||
direction.y = 1
|
||||
elif pos_a.y > pos_b.y:
|
||||
direction.y = -1
|
||||
|
||||
return direction
|
||||
|
||||
|
||||
# SECTION: game tick
|
||||
|
||||
func tick_handle_temperature(cell_temperature: int):
|
||||
if cell_temperature == 0:
|
||||
temperature_timer = 0
|
||||
elif temperature_timer > temperature_endure:
|
||||
temperature_timer += cell_temperature
|
||||
health -= temperature_damage
|
||||
|
||||
|
||||
func tick_handle_food():
|
||||
if food <= 0:
|
||||
health -= food_damage;
|
||||
pass
|
||||
|
||||
|
||||
func game_tick() -> void:
|
||||
behavior_tree.game_tick()
|
||||
|
||||
var player_positon_array: Array[Vector2i] = game_manager.world.tilemap_player.get_cells_by_type(tilemap_types.PLAYER)
|
||||
if len(player_positon_array) > 0:
|
||||
var player_positon: Vector2i = player_positon_array[0]
|
||||
var cell_temperature: int = game_manager.world.tilemap_temperature.get_custom_data(player_positon, "temperature", 0) as int
|
||||
tick_handle_temperature(cell_temperature)
|
||||
else:
|
||||
push_error("No player found on tilemap")
|
||||
|
||||
tick_handle_food()
|
||||
|
||||
if health < 0:
|
||||
game_manager.player_health_depleted()
|
||||
|
|
|
|||
|
|
@ -22,8 +22,10 @@ func get_cells_by_custom_data(field_name: String, custom_data: Variant) -> Array
|
|||
return tiles_with_custom_data
|
||||
|
||||
|
||||
func get_custom_data(coords: Vector2i, field_name: String) -> Variant:
|
||||
func get_custom_data(coords: Vector2i, field_name: String, default_value: Variant) -> Variant:
|
||||
var tile_data: TileData = tilemap.get_cell_tile_data(coords)
|
||||
if not tile_data:
|
||||
return default_value
|
||||
return tile_data.get_custom_data(field_name)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
class_name TileMapTileTypes
|
||||
|
||||
# global values
|
||||
const EMPTY: Vector2i = Vector2i(-1, -1)
|
||||
#
|
||||
# ground, sid = 0
|
||||
const GROUND_GRASS: Vector2i = Vector2i(0, 0)
|
||||
const GROUND_WATER_SHALLOW: Vector2i = Vector2i(1, 0)
|
||||
|
|
@ -7,8 +10,11 @@ const GROUND_WATER_DEEP: Vector2i = Vector2i(2, 0)
|
|||
const GROUND_SAND: Vector2i = Vector2i(3, 0)
|
||||
#
|
||||
# objects, sid = 1
|
||||
const OBJECT_RANDOM_1: Vector2i = Vector2i(0, 0) # testing only, to be removed
|
||||
const OBJECT_RANDOM_2: Vector2i = Vector2i(1, 0) # testing only, to be removed
|
||||
# NI = not interactive
|
||||
const OBJECT_NI_RANDOM_1: Vector2i = Vector2i(0, 0) # testing only, to be removed
|
||||
const OBJECT_NI_RANDOM_2: Vector2i = Vector2i(1, 0) # testing only, to be removed
|
||||
const OBJECT_NI_ROCK_1: Vector2i = Vector2i(2, 0)
|
||||
# I = interactive
|
||||
#
|
||||
# temperature, sid = 2
|
||||
const TEMPERATURE_NORMAL: Vector2i = Vector2i(-1, -1)
|
||||
|
|
|
|||
|
|
@ -28,10 +28,73 @@ func _ready() -> void:
|
|||
tilemap_player.setup()
|
||||
tilemap_temperature.setup()
|
||||
|
||||
# example usage
|
||||
# tilemap_temperature.fill_area(Vector2i(0, 0), Vector2i(10, 10), tilemap_types.TEMPERATURE_COLD_1)
|
||||
# tilemap_temperature.fill_area(Vector2i(4, 4), Vector2i(6, 6), tilemap_types.TEMPERATURE_NORMAL)
|
||||
# print(tilemap_non_interactive.get_cells_by_custom_data("walkable", true))
|
||||
# tilemap_ground.clear_cells()
|
||||
# tilemap_ground.set_cell(Vector2i(0, 0), tilemap_types.GROUND_GRASS)
|
||||
# print(tilemap_ground.local_to_cell(get_local_mouse_position()))
|
||||
|
||||
# example usage
|
||||
# tilemap_temperature.fill_area(Vector2i(0, 0), Vector2i(10, 10), tilemap_types.TEMPERATURE_COLD_1)
|
||||
# tilemap_temperature.fill_area(Vector2i(4, 4), Vector2i(6, 6), tilemap_types.TEMPERATURE_NORMAL)
|
||||
# print(tilemap_non_interactive.get_cells_by_custom_data("walkable", true))
|
||||
# tilemap_ground.clear_cells()
|
||||
# tilemap_ground.set_cell(Vector2i(0, 0), tilemap_types.GROUND_GRASS)
|
||||
# print(tilemap_ground.local_to_cell(get_local_mouse_position()))
|
||||
|
||||
func is_walkable(position: Vector2i) -> bool:
|
||||
var ground_tile_walkable: bool = tilemap_ground.get_custom_data(position, "walkable", false)
|
||||
if not ground_tile_walkable:
|
||||
return false
|
||||
|
||||
var non_interactive_tile: TileData = tilemap_non_interactive.get_cell(position)
|
||||
if non_interactive_tile:
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
var f_score: Dictionary = {}
|
||||
|
||||
|
||||
func find_path(start_position: Vector2i, end_position: Vector2i) -> Array[Vector2i]:
|
||||
var open_list: Array = []
|
||||
var came_from: Dictionary = {}
|
||||
var g_score: Dictionary = {}
|
||||
f_score = {}
|
||||
|
||||
open_list.append(start_position)
|
||||
g_score[start_position] = 0
|
||||
f_score[start_position] = start_position.distance_to(end_position)
|
||||
|
||||
while open_list.size() > 0:
|
||||
open_list.sort_custom(Callable(self, "_compare_f_score"))
|
||||
var current: Vector2i = open_list[0]
|
||||
|
||||
if current == end_position:
|
||||
var path: Array[Vector2i] = []
|
||||
while current in came_from:
|
||||
path.insert(0, current)
|
||||
current = came_from[current]
|
||||
path.insert(0, start_position)
|
||||
return path
|
||||
|
||||
open_list.erase(current)
|
||||
|
||||
for direction in [Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0)]:
|
||||
var neighbor: Vector2i = current + direction
|
||||
|
||||
if not is_walkable(neighbor):
|
||||
continue
|
||||
|
||||
var cost: int = tilemap_ground.get_custom_data(neighbor, "cost", 1)
|
||||
var tentative_g_score: int = g_score.get(current, INF) + cost
|
||||
|
||||
if tentative_g_score < g_score.get(neighbor, INF):
|
||||
came_from[neighbor] = current
|
||||
g_score[neighbor] = tentative_g_score
|
||||
f_score[neighbor] = tentative_g_score + neighbor.distance_to(end_position)
|
||||
if not neighbor in open_list:
|
||||
open_list.append(neighbor)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
func _compare_f_score(a: Vector2i, b: Vector2i) -> int:
|
||||
var score_a: float = f_score.get(a, INF)
|
||||
var score_b: float = f_score.get(b, INF)
|
||||
return score_a - score_b
|
||||
|
|
|
|||
Loading…
Reference in New Issue