Trying new stuff with the camera and grid. I want to be able to move a unit but there is a problem with the grid at the moment. I am now trying to implement a new way of handling the camera, maybe then I can move on to moving units...
This commit is contained in:
67
Resource/Grid.gd
Normal file
67
Resource/Grid.gd
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
extends Resource
|
||||||
|
|
||||||
|
class_name Grid
|
||||||
|
|
||||||
|
# The grid size.
|
||||||
|
@export var size := Vector2(100, 100)
|
||||||
|
# The of a cell in pixels
|
||||||
|
@export var cellSize := Vector2(16, 16)
|
||||||
|
|
||||||
|
@export var cameraPosition := Vector2.ONE
|
||||||
|
func setCameraPosition(pos: Vector2):
|
||||||
|
cameraPosition = pos
|
||||||
|
@export var cameraZoom := Vector2(2, 2)
|
||||||
|
func setCameraZoom(zoom: Vector2):
|
||||||
|
cameraZoom = zoom
|
||||||
|
@export var screenCenter := Vector2.ZERO
|
||||||
|
func setScreenCenter(pos: Vector2):
|
||||||
|
screenCenter = pos
|
||||||
|
|
||||||
|
# Half of ``cell_size``.
|
||||||
|
# We will use this to calculate the center of a grid cell in pixels, on the screen.
|
||||||
|
# That's how we can place units in the center of a cell.
|
||||||
|
var _halfCellSize: Vector2 = cellSize / 2
|
||||||
|
|
||||||
|
# Returns the position of a cell's center in pixels.
|
||||||
|
# We'll place units and have them move through cells using this function.
|
||||||
|
func calculateMapPosition(gridPosition: Vector2) -> Vector2:
|
||||||
|
return cameraPosition + (gridPosition * cellSize + _halfCellSize) / cameraZoom
|
||||||
|
|
||||||
|
# Returns the coordinates of the cell on the grid given a position on the map.
|
||||||
|
# This is the complementary of `calculate_map_position()` above.
|
||||||
|
# When designing a level, you'll place units visually in the editor. We'll use this function to find
|
||||||
|
# the grid coordinates they're placed on, and call `calculate_map_position()` to snap them to the
|
||||||
|
# cell's center.
|
||||||
|
func calculateGridCoordinates(mapPosition: Vector2) -> Vector2:
|
||||||
|
return (mapPosition / cellSize).floor()
|
||||||
|
|
||||||
|
|
||||||
|
# Returns true if the `cell_coordinates` are within the grid.
|
||||||
|
# This method and the following one allow us to ensure the cursor or units can never go past the
|
||||||
|
# map's limit.
|
||||||
|
func isWithinBounds(cellCoordinates: Vector2) -> bool:
|
||||||
|
var out := cellCoordinates.x >= 0 and cellCoordinates.x < size.x
|
||||||
|
return out and cellCoordinates.y >= 0 and cellCoordinates.y < size.y
|
||||||
|
|
||||||
|
|
||||||
|
# Makes the `grid_position` fit within the grid's bounds.
|
||||||
|
# This is a clamp function designed specifically for our grid coordinates.
|
||||||
|
# The Vector2 class comes with its `Vector2.clamp()` method, but it doesn't work the same way: it
|
||||||
|
# limits the vector's length instead of clamping each of the vector's components individually.
|
||||||
|
# That's why we need to code a new method.
|
||||||
|
func clamp(gridPosition: Vector2) -> Vector2:
|
||||||
|
var out := gridPosition
|
||||||
|
out.x = clamp(out.x, 0, size.x - 1.0)
|
||||||
|
out.y = clamp(out.y, 0, size.y - 1.0)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# Given Vector2 coordinates, calculates and returns the corresponding integer index. You can use
|
||||||
|
# this function to convert 2D coordinates to a 1D array's indices.
|
||||||
|
#
|
||||||
|
# There are two cases where you need to convert coordinates like so:
|
||||||
|
# 1. We'll need it for the AStar algorithm, which requires a unique index for each point on the
|
||||||
|
# graph it uses to find a path.
|
||||||
|
# 2. You can use it for performance. More on that below.
|
||||||
|
func asIndex(cell: Vector2) -> int:
|
||||||
|
return int(cell.x + size.x * cell.y)
|
||||||
1
Resource/Grid.gd.uid
Normal file
1
Resource/Grid.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://blwwie08bb4s
|
||||||
9
Resource/Grid.tres
Normal file
9
Resource/Grid.tres
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[gd_resource type="Resource" script_class="Grid" load_steps=2 format=3 uid="uid://bpf7mj7w5kftq"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://blwwie08bb4s" path="res://Resource/Grid.gd" id="1_ubiq0"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_ubiq0")
|
||||||
|
size = Vector2(60, 60)
|
||||||
|
cellSize = Vector2(16, 16)
|
||||||
|
metadata/_custom_type_script = "uid://blwwie08bb4s"
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
|
# To change the zoom
|
||||||
|
@export var grid: Resource
|
||||||
|
|
||||||
|
@export var CameraSpeedMult = 2
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
pass # Replace with function body.
|
pass # Replace with function body.
|
||||||
@@ -8,15 +13,24 @@ func _ready() -> void:
|
|||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
if Input.is_action_pressed("MoveUp"):
|
if Input.is_action_pressed("MoveUp"):
|
||||||
global_position += Vector2.UP * 2
|
_moveCamera(Vector2.UP)
|
||||||
if Input.is_action_pressed("MoveDown"):
|
if Input.is_action_pressed("MoveDown"):
|
||||||
global_position += Vector2.DOWN * 2
|
_moveCamera(Vector2.DOWN)
|
||||||
if Input.is_action_pressed("MoveLeft"):
|
if Input.is_action_pressed("MoveLeft"):
|
||||||
global_position += Vector2.LEFT * 2
|
_moveCamera(Vector2.LEFT)
|
||||||
if Input.is_action_pressed("MoveRight"):
|
if Input.is_action_pressed("MoveRight"):
|
||||||
global_position += Vector2.RIGHT * 2
|
_moveCamera(Vector2.RIGHT)
|
||||||
|
|
||||||
if Input.is_action_just_released("ZoomIn"):
|
if Input.is_action_just_released("ZoomIn"):
|
||||||
$SmartCamera2D.zoom += Vector2.ONE
|
_zoomCamera(Vector2.ONE)
|
||||||
if Input.is_action_just_released("ZoomOut"):
|
if Input.is_action_just_released("ZoomOut"):
|
||||||
$SmartCamera2D.zoom -= Vector2.ONE
|
_zoomCamera(-Vector2.ONE)
|
||||||
|
|
||||||
|
func _moveCamera(direction: Vector2):
|
||||||
|
position += direction * CameraSpeedMult
|
||||||
|
grid.setCameraPosition($SmartCamera2D.position)
|
||||||
|
grid.setScreenCenter($SmartCamera2D.get_screen_center_position())
|
||||||
|
|
||||||
|
func _zoomCamera(direction: Vector2):
|
||||||
|
$SmartCamera2D.zoom += direction
|
||||||
|
grid.setCameraZoom($SmartCamera2D.zoom)
|
||||||
|
|||||||
@@ -9,6 +9,11 @@ script = ExtResource("1_ig7ij")
|
|||||||
metadata/_edit_group_ = true
|
metadata/_edit_group_ = true
|
||||||
|
|
||||||
[node name="SmartCamera2D" type="Camera2D" parent="."]
|
[node name="SmartCamera2D" type="Camera2D" parent="."]
|
||||||
|
limit_left = 0
|
||||||
|
limit_top = 0
|
||||||
|
limit_right = 800
|
||||||
|
limit_bottom = 480
|
||||||
|
limit_smoothed = true
|
||||||
position_smoothing_enabled = true
|
position_smoothing_enabled = true
|
||||||
script = ExtResource("2_du7i2")
|
script = ExtResource("2_du7i2")
|
||||||
target = NodePath("..")
|
target = NodePath("..")
|
||||||
|
|||||||
75
Scenes/Main/cursor.gd
Normal file
75
Scenes/Main/cursor.gd
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
## Player-controlled cursor. Allows them to navigate the game grid, select units, and move them.
|
||||||
|
## Supports both keyboard and mouse (or touch) input.
|
||||||
|
@tool
|
||||||
|
class_name Cursor
|
||||||
|
extends Node2D
|
||||||
|
|
||||||
|
## Emitted when clicking on the currently hovered cell or when pressing "ui_accept".
|
||||||
|
signal accept_pressed(cell)
|
||||||
|
## Emitted when the cursor moved to a new cell.
|
||||||
|
signal moved(new_cell)
|
||||||
|
|
||||||
|
## Grid resource, giving the node access to the grid size, and more.
|
||||||
|
@export var grid: Resource
|
||||||
|
## Time before the cursor can move again in seconds.
|
||||||
|
@export var ui_cooldown := 0.1
|
||||||
|
|
||||||
|
## Coordinates of the current cell the cursor is hovering.
|
||||||
|
var cell := Vector2.ZERO:
|
||||||
|
set(value):
|
||||||
|
print("Setting cell to: ", value)
|
||||||
|
# We first clamp the cell coordinates and ensure that we aren't
|
||||||
|
# trying to move outside the grid boundaries
|
||||||
|
var new_cell: Vector2 = grid.clamp(value)
|
||||||
|
print("New cell: ", new_cell)
|
||||||
|
if new_cell.is_equal_approx(cell):
|
||||||
|
return
|
||||||
|
|
||||||
|
cell = new_cell
|
||||||
|
print("Cell is ", cell)
|
||||||
|
# If we move to a new cell, we update the cursor's position, emit
|
||||||
|
# a signal, and start the cooldown timer that will limit the rate
|
||||||
|
# at which the cursor moves when we keep the direction key held
|
||||||
|
# down
|
||||||
|
position = grid.calculateMapPosition(cell)
|
||||||
|
print("Position is ", position)
|
||||||
|
emit_signal("moved", cell)
|
||||||
|
_timer.start()
|
||||||
|
|
||||||
|
@onready var _timer: Timer = $Timer
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_timer.wait_time = ui_cooldown
|
||||||
|
position = grid.calculateMapPosition(cell)
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
# Navigating cells with the mouse.
|
||||||
|
if event is InputEventMouseMotion:
|
||||||
|
cell = grid.calculateGridCoordinates(event.position)
|
||||||
|
# Trying to select something in a cell.
|
||||||
|
elif event.is_action_pressed("Select") or event.is_action_pressed("ui_accept"):
|
||||||
|
emit_signal("accept_pressed", cell)
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
|
||||||
|
var should_move := event.is_pressed()
|
||||||
|
if event.is_echo():
|
||||||
|
should_move = should_move and _timer.is_stopped()
|
||||||
|
|
||||||
|
if not should_move:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Moves the cursor by one grid cell.
|
||||||
|
if event.is_action("ui_right"):
|
||||||
|
cell += Vector2.RIGHT
|
||||||
|
elif event.is_action("ui_up"):
|
||||||
|
cell += Vector2.UP
|
||||||
|
elif event.is_action("ui_left"):
|
||||||
|
cell += Vector2.LEFT
|
||||||
|
elif event.is_action("ui_down"):
|
||||||
|
cell += Vector2.DOWN
|
||||||
|
|
||||||
|
|
||||||
|
func _draw() -> void:
|
||||||
|
draw_rect(Rect2(-grid.cellSize / 2, grid.cellSize), Color.ALICE_BLUE, false, 2.0)
|
||||||
1
Scenes/Main/cursor.gd.uid
Normal file
1
Scenes/Main/cursor.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cidjtc27oj1gn
|
||||||
142
Scenes/Main/game_board.gd
Normal file
142
Scenes/Main/game_board.gd
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
## Represents and manages the game board. Stores references to entities that are in each cell and
|
||||||
|
## tells whether cells are occupied or not.
|
||||||
|
## Units can only move around the grid one at a time.
|
||||||
|
class_name GameBoard
|
||||||
|
extends Node2D
|
||||||
|
|
||||||
|
const DIRECTIONS = [Vector2.LEFT, Vector2.RIGHT, Vector2.UP, Vector2.DOWN]
|
||||||
|
|
||||||
|
## Resource of type Grid.
|
||||||
|
@export var grid: Resource
|
||||||
|
|
||||||
|
## Mapping of coordinates of a cell to a reference to the unit it contains.
|
||||||
|
var _units := {}
|
||||||
|
var _active_unit: Unit
|
||||||
|
var _walkable_cells := []
|
||||||
|
|
||||||
|
#@onready var _unit_overlay: UnitOverlay = $UnitOverlay
|
||||||
|
#@onready var _unit_path: UnitPath = $UnitPath
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_reinitialize()
|
||||||
|
|
||||||
|
|
||||||
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
|
if _active_unit and event.is_action_pressed("ui_cancel"):
|
||||||
|
_deselect_active_unit()
|
||||||
|
_clear_active_unit()
|
||||||
|
|
||||||
|
|
||||||
|
func _get_configuration_warning() -> String:
|
||||||
|
var warning := ""
|
||||||
|
if not grid:
|
||||||
|
warning = "You need a Grid resource for this node to work."
|
||||||
|
return warning
|
||||||
|
|
||||||
|
|
||||||
|
## Returns `true` if the cell is occupied by a unit.
|
||||||
|
func is_occupied(cell: Vector2) -> bool:
|
||||||
|
return _units.has(cell)
|
||||||
|
|
||||||
|
|
||||||
|
## Returns an array of cells a given unit can walk using the flood fill algorithm.
|
||||||
|
func get_walkable_cells(unit: Unit) -> Array:
|
||||||
|
return _flood_fill(unit.cell, unit.move_range)
|
||||||
|
|
||||||
|
|
||||||
|
## Clears, and refills the `_units` dictionary with game objects that are on the board.
|
||||||
|
func _reinitialize() -> void:
|
||||||
|
_units.clear()
|
||||||
|
|
||||||
|
for child in get_children():
|
||||||
|
var unit := child as Unit
|
||||||
|
if not unit:
|
||||||
|
continue
|
||||||
|
_units[unit.cell] = unit
|
||||||
|
|
||||||
|
|
||||||
|
## Returns an array with all the coordinates of walkable cells based on the `max_distance`.
|
||||||
|
func _flood_fill(cell: Vector2, max_distance: int) -> Array:
|
||||||
|
var array := []
|
||||||
|
var stack := [cell]
|
||||||
|
while not stack.size() == 0:
|
||||||
|
var current = stack.pop_back()
|
||||||
|
if not grid.is_within_bounds(current):
|
||||||
|
continue
|
||||||
|
if current in array:
|
||||||
|
continue
|
||||||
|
|
||||||
|
var difference: Vector2 = (current - cell).abs()
|
||||||
|
var distance := int(difference.x + difference.y)
|
||||||
|
if distance > max_distance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
array.append(current)
|
||||||
|
for direction in DIRECTIONS:
|
||||||
|
var coordinates: Vector2 = current + direction
|
||||||
|
if is_occupied(coordinates):
|
||||||
|
continue
|
||||||
|
if coordinates in array:
|
||||||
|
continue
|
||||||
|
# Minor optimization: If this neighbor is already queued
|
||||||
|
# to be checked, we don't need to queue it again
|
||||||
|
if coordinates in stack:
|
||||||
|
continue
|
||||||
|
|
||||||
|
stack.append(coordinates)
|
||||||
|
return array
|
||||||
|
|
||||||
|
|
||||||
|
## Updates the _units dictionary with the target position for the unit and asks the _active_unit to walk to it.
|
||||||
|
func _move_active_unit(new_cell: Vector2) -> void:
|
||||||
|
if is_occupied(new_cell) or not new_cell in _walkable_cells:
|
||||||
|
return
|
||||||
|
# warning-ignore:return_value_discarded
|
||||||
|
_units.erase(_active_unit.cell)
|
||||||
|
_units[new_cell] = _active_unit
|
||||||
|
_deselect_active_unit()
|
||||||
|
# _active_unit.walk_along(_unit_path.current_path)
|
||||||
|
await _active_unit.walk_finished
|
||||||
|
_clear_active_unit()
|
||||||
|
|
||||||
|
|
||||||
|
## Selects the unit in the `cell` if there's one there.
|
||||||
|
## Sets it as the `_active_unit` and draws its walkable cells and interactive move path.
|
||||||
|
func _select_unit(cell: Vector2) -> void:
|
||||||
|
if not _units.has(cell):
|
||||||
|
return
|
||||||
|
|
||||||
|
_active_unit = _units[cell]
|
||||||
|
_active_unit.is_selected = true
|
||||||
|
_walkable_cells = get_walkable_cells(_active_unit)
|
||||||
|
# _unit_overlay.draw(_walkable_cells)
|
||||||
|
# _unit_path.initialize(_walkable_cells)
|
||||||
|
|
||||||
|
|
||||||
|
## Deselects the active unit, clearing the cells overlay and interactive path drawing.
|
||||||
|
func _deselect_active_unit() -> void:
|
||||||
|
_active_unit.is_selected = false
|
||||||
|
# _unit_overlay.clear()
|
||||||
|
# _unit_path.stop()
|
||||||
|
|
||||||
|
|
||||||
|
## Clears the reference to the _active_unit and the corresponding walkable cells.
|
||||||
|
func _clear_active_unit() -> void:
|
||||||
|
_active_unit = null
|
||||||
|
_walkable_cells.clear()
|
||||||
|
|
||||||
|
|
||||||
|
## Selects or moves a unit based on where the cursor is.
|
||||||
|
func _on_Cursor_accept_pressed(cell: Vector2) -> void:
|
||||||
|
if not _active_unit:
|
||||||
|
_select_unit(cell)
|
||||||
|
elif _active_unit.is_selected:
|
||||||
|
_move_active_unit(cell)
|
||||||
|
|
||||||
|
|
||||||
|
## Updates the interactive path's drawing if there's an active and selected unit.
|
||||||
|
func _on_Cursor_moved(new_cell: Vector2) -> void:
|
||||||
|
# if _active_unit and _active_unit.is_selected:
|
||||||
|
# _unit_path.draw(_active_unit.cell, new_cell)
|
||||||
|
pass
|
||||||
1
Scenes/Main/game_board.gd.uid
Normal file
1
Scenes/Main/game_board.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://14cwbxcvt5dx
|
||||||
@@ -15,12 +15,14 @@ var _MovingMarker: Node2D = _MovingMarkerScene.instantiate()
|
|||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
add_child(_MovingMarker)
|
# add_child(_MovingMarker)
|
||||||
_MovingMarker.hide()
|
# _MovingMarker.hide()
|
||||||
|
#
|
||||||
|
# _createUnit(Vector2i(10,10))
|
||||||
|
# for unit in _Units:
|
||||||
|
# add_child(unit)
|
||||||
|
|
||||||
_createUnit(Vector2i(10,10))
|
$CameraController.position = get_viewport().get_camera_2d().get_screen_center_position()
|
||||||
for unit in _Units:
|
|
||||||
add_child(unit)
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://d05j5yuhlsxp0"]
|
[gd_scene load_steps=9 format=3 uid="uid://d05j5yuhlsxp0"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://cywuuce71rmgb" path="res://Scenes/Map/map.tscn" id="1_1r6ip"]
|
[ext_resource type="PackedScene" uid="uid://cywuuce71rmgb" path="res://Scenes/Map/map.tscn" id="1_1r6ip"]
|
||||||
[ext_resource type="Script" uid="uid://btdvxp8ckmeb3" path="res://Scenes/Main/main.gd" id="1_qw60k"]
|
[ext_resource type="Script" uid="uid://btdvxp8ckmeb3" path="res://Scenes/Main/main.gd" id="1_qw60k"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bfvijh611aggp" path="res://Scenes/Camera/camera_controller.tscn" id="3_qw60k"]
|
[ext_resource type="PackedScene" uid="uid://bfvijh611aggp" path="res://Scenes/Camera/camera_controller.tscn" id="3_qw60k"]
|
||||||
|
[ext_resource type="Script" uid="uid://14cwbxcvt5dx" path="res://Scenes/Main/game_board.gd" id="4_5yls4"]
|
||||||
|
[ext_resource type="Resource" uid="uid://bpf7mj7w5kftq" path="res://Resource/Grid.tres" id="5_p6jpk"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://b1d6lktijxy3s" path="res://Scenes/Unit/move/unit.tscn" id="6_2a143"]
|
||||||
|
[ext_resource type="Script" uid="uid://cidjtc27oj1gn" path="res://Scenes/Main/cursor.gd" id="7_y3v7k"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bckknh8k5fh1s" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0448.png" id="8_hryqi"]
|
||||||
|
|
||||||
[node name="Main" type="Node"]
|
[node name="Main" type="Node"]
|
||||||
script = ExtResource("1_qw60k")
|
script = ExtResource("1_qw60k")
|
||||||
@@ -10,3 +15,24 @@ script = ExtResource("1_qw60k")
|
|||||||
[node name="Map" parent="." instance=ExtResource("1_1r6ip")]
|
[node name="Map" parent="." instance=ExtResource("1_1r6ip")]
|
||||||
|
|
||||||
[node name="CameraController" parent="." instance=ExtResource("3_qw60k")]
|
[node name="CameraController" parent="." instance=ExtResource("3_qw60k")]
|
||||||
|
grid = ExtResource("5_p6jpk")
|
||||||
|
|
||||||
|
[node name="GameBoard" type="Node2D" parent="."]
|
||||||
|
script = ExtResource("4_5yls4")
|
||||||
|
grid = ExtResource("5_p6jpk")
|
||||||
|
|
||||||
|
[node name="Unit" parent="GameBoard" instance=ExtResource("6_2a143")]
|
||||||
|
grid = ExtResource("5_p6jpk")
|
||||||
|
|
||||||
|
[node name="Cursor" type="Node2D" parent="GameBoard"]
|
||||||
|
script = ExtResource("7_y3v7k")
|
||||||
|
grid = ExtResource("5_p6jpk")
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="GameBoard/Cursor"]
|
||||||
|
position = Vector2(10, -10)
|
||||||
|
texture = ExtResource("8_hryqi")
|
||||||
|
|
||||||
|
[node name="Timer" type="Timer" parent="GameBoard/Cursor"]
|
||||||
|
wait_time = 0.1
|
||||||
|
|
||||||
|
[editable path="CameraController"]
|
||||||
|
|||||||
87
Scenes/Unit/move/unit.tscn
Normal file
87
Scenes/Unit/move/unit.tscn
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
[gd_scene load_steps=11 format=3 uid="uid://b1d6lktijxy3s"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://c8ocnhejcdc77" path="res://unit.gd" id="1_astap"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://cgvyfsuri6vmx" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tilemap/tilemap.png" id="2_fhoaw"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dlaevn54qcvej" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0267.png" id="3_fhoaw"]
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_4o1a4"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(408, 51, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_hn0wa"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(408, 68, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_jt11o"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(408, 85, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_e33ge"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(408, 68, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_px3ay"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(408, 85, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="AtlasTexture" id="AtlasTexture_6ceyn"]
|
||||||
|
atlas = ExtResource("2_fhoaw")
|
||||||
|
region = Rect2(34, 255, 16, 16)
|
||||||
|
|
||||||
|
[sub_resource type="SpriteFrames" id="SpriteFrames_lgeeq"]
|
||||||
|
animations = [{
|
||||||
|
"frames": [],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"default",
|
||||||
|
"speed": 5.0
|
||||||
|
}, {
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_4o1a4")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_hn0wa")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_jt11o")
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"down",
|
||||||
|
"speed": 5.0
|
||||||
|
}, {
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_e33ge")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_px3ay")
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"idle",
|
||||||
|
"speed": 5.0
|
||||||
|
}, {
|
||||||
|
"frames": [{
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": SubResource("AtlasTexture_6ceyn")
|
||||||
|
}, {
|
||||||
|
"duration": 1.0,
|
||||||
|
"texture": null
|
||||||
|
}],
|
||||||
|
"loop": true,
|
||||||
|
"name": &"selected",
|
||||||
|
"speed": 5.0
|
||||||
|
}]
|
||||||
|
|
||||||
|
[node name="Unit" type="Path2D"]
|
||||||
|
script = ExtResource("1_astap")
|
||||||
|
|
||||||
|
[node name="PathFollow2D" type="PathFollow2D" parent="."]
|
||||||
|
rotates = false
|
||||||
|
|
||||||
|
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="PathFollow2D"]
|
||||||
|
visible = false
|
||||||
|
sprite_frames = SubResource("SpriteFrames_lgeeq")
|
||||||
|
animation = &"down"
|
||||||
|
|
||||||
|
[node name="Sprite2D" type="Sprite2D" parent="PathFollow2D"]
|
||||||
|
texture = ExtResource("3_fhoaw")
|
||||||
@@ -2,36 +2,60 @@ extends Node2D
|
|||||||
|
|
||||||
class_name Unit
|
class_name Unit
|
||||||
|
|
||||||
|
signal walk_finished
|
||||||
|
|
||||||
|
@export var grid: Resource
|
||||||
|
|
||||||
var _spawnPosition: Vector2
|
var _spawnPosition: Vector2
|
||||||
|
|
||||||
### Marker
|
|
||||||
# For now the marker will be spawned and deleted by the unit.
|
|
||||||
# Later it will be handled by the main scene.
|
|
||||||
# Load marker scene.
|
|
||||||
# var marker_scene: PackedScene = preload("res://Scenes/Unit/marker.tscn")
|
|
||||||
# var marker
|
|
||||||
## WE NOW USE A SIMPLE SPRITE2D FOR THE MARKER
|
|
||||||
|
|
||||||
|
|
||||||
@onready var _readyToSelectMarker: Sprite2D = $ReadyToSelectMarker
|
@onready var _readyToSelectMarker: Sprite2D = $ReadyToSelectMarker
|
||||||
@onready var _selectedMarker: Sprite2D = $SelectedMarker
|
@onready var _selectedMarker: Sprite2D = $SelectedMarker
|
||||||
#@onready var _movingMarker: Sprite2D = $MovingMarker
|
|
||||||
|
|
||||||
var _readyToSelect: bool = false
|
var _readyToSelect: bool = false
|
||||||
var _selected: bool = false
|
|
||||||
#var _moving: bool = false
|
var isSelected := false:
|
||||||
|
set(value):
|
||||||
|
isSelected = value
|
||||||
|
if isSelected: _selectUnit()
|
||||||
|
else: _deselectUnit()
|
||||||
|
|
||||||
var TargetPosition: Vector2
|
var TargetPosition: Vector2
|
||||||
|
var _isMoving := false:
|
||||||
|
set(value):
|
||||||
|
_isMoving = value
|
||||||
|
set_process(_isMoving)
|
||||||
|
|
||||||
|
## Unit Data
|
||||||
|
### Distance in cells
|
||||||
|
@export var moveRange := 6
|
||||||
|
### Speed along the path
|
||||||
|
@export var moveSpeed := 600.0
|
||||||
|
|
||||||
|
# Coordinates of the current cell the cursor moved to
|
||||||
|
var cell := Vector2.ZERO:
|
||||||
|
set(value):
|
||||||
|
# When changing the cell's value, we don't want to allow coordinates outside the grid.
|
||||||
|
cell = grid.clamp(value)
|
||||||
|
@onready var _path: Path2D = $Path2D
|
||||||
|
@onready var _pathFollow: PathFollow2D = $Path2D/PathFollow2D
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# marker = marker_scene.instantiate()
|
# Spawning
|
||||||
# marker.hide()
|
|
||||||
global_position = _spawnPosition
|
global_position = _spawnPosition
|
||||||
|
|
||||||
_readyToSelectMarker.hide()
|
_readyToSelectMarker.hide()
|
||||||
_selectedMarker.hide()
|
_selectedMarker.hide()
|
||||||
# _movingMarker.hide()
|
|
||||||
|
# Pathing and Grid
|
||||||
|
set_process(false)
|
||||||
|
_pathFollow.rotates = false
|
||||||
|
|
||||||
|
cell = grid.calculateGridCoordinates(position)
|
||||||
|
position = grid.calculateMapPosition(cell)
|
||||||
|
|
||||||
|
# We create the curve resource here because creating it in the editor prevents
|
||||||
|
# us from moving the unit.
|
||||||
|
if not Engine.is_editor_hint():
|
||||||
|
_path.curve = Curve2D.new()
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
@@ -42,31 +66,13 @@ func _input(event: InputEvent):
|
|||||||
print_debug("Action is Select")
|
print_debug("Action is Select")
|
||||||
|
|
||||||
# We combine it with the fact that it is already marked (@see _markUnit)
|
# We combine it with the fact that it is already marked (@see _markUnit)
|
||||||
if _readyToSelect: _selectUnit()
|
if _readyToSelect: isSelected = true
|
||||||
else: _deselectUnit()
|
else: isSelected = false
|
||||||
|
|
||||||
|
|
||||||
# if event.is_action_pressed("SetMarker"):
|
|
||||||
# print("Action is SetMarker")
|
|
||||||
# if _selected:
|
|
||||||
# print("Setting marker to ", event.position)
|
|
||||||
# #marker.global_position = event.position
|
|
||||||
## marker.show()
|
|
||||||
## _movingMarker.position = get_global_mouse_position()
|
|
||||||
## _movingMarker.show()
|
|
||||||
|
|
||||||
func _getUnitPosition():
|
|
||||||
return $AnimatedSprite2D.global_position
|
|
||||||
|
|
||||||
func _setSelected(selected: bool):
|
|
||||||
_selected = selected
|
|
||||||
|
|
||||||
func _selectUnit():
|
func _selectUnit():
|
||||||
_setSelected(true)
|
|
||||||
_selectedMarker.show()
|
_selectedMarker.show()
|
||||||
|
|
||||||
func _deselectUnit():
|
func _deselectUnit():
|
||||||
_setSelected(false)
|
|
||||||
_selectedMarker.hide()
|
_selectedMarker.hide()
|
||||||
|
|
||||||
func _gets_selected(viewport: Node, event: InputEvent, shape_index: int):
|
func _gets_selected(viewport: Node, event: InputEvent, shape_index: int):
|
||||||
@@ -82,4 +88,4 @@ func _unMarkUnit():
|
|||||||
_readyToSelectMarker.hide()
|
_readyToSelectMarker.hide()
|
||||||
|
|
||||||
func moveToTarget():
|
func moveToTarget():
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
[gd_scene load_steps=92 format=3 uid="uid://dy7rltpxyqyw7"]
|
[gd_scene load_steps=93 format=3 uid="uid://dy7rltpxyqyw7"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://dpu6c0bpm0dvl" path="res://Scenes/Unit/unit.gd" id="1_15sed"]
|
[ext_resource type="Script" uid="uid://dpu6c0bpm0dvl" path="res://Scenes/Unit/unit.gd" id="1_15sed"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cgvyfsuri6vmx" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tilemap/tilemap.png" id="1_hgpyh"]
|
[ext_resource type="Texture2D" uid="uid://cgvyfsuri6vmx" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tilemap/tilemap.png" id="1_hgpyh"]
|
||||||
|
[ext_resource type="Resource" uid="uid://bpf7mj7w5kftq" path="res://Resource/Grid.tres" id="2_jbdwb"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bprproedmlhtr" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0168.png" id="3_ladk0"]
|
[ext_resource type="Texture2D" uid="uid://bprproedmlhtr" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0168.png" id="3_ladk0"]
|
||||||
[ext_resource type="Texture2D" uid="uid://b7ra2w7rdeqij" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0169.png" id="4_iuf4a"]
|
[ext_resource type="Texture2D" uid="uid://b7ra2w7rdeqij" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0169.png" id="4_iuf4a"]
|
||||||
[ext_resource type="Texture2D" uid="uid://cxtkb8rqq0j6r" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0407.png" id="5_ulevp"]
|
[ext_resource type="Texture2D" uid="uid://cxtkb8rqq0j6r" path="res://Graphics/TileMaps/kenney_rpgUrbanKit/Tiles/tile_0407.png" id="5_ulevp"]
|
||||||
@@ -628,6 +629,7 @@ size = Vector2(16, 16)
|
|||||||
[node name="Unit" type="Node2D"]
|
[node name="Unit" type="Node2D"]
|
||||||
texture_filter = 1
|
texture_filter = 1
|
||||||
script = ExtResource("1_15sed")
|
script = ExtResource("1_15sed")
|
||||||
|
grid = ExtResource("2_jbdwb")
|
||||||
|
|
||||||
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."]
|
||||||
sprite_frames = SubResource("SpriteFrames_7f253")
|
sprite_frames = SubResource("SpriteFrames_7f253")
|
||||||
@@ -653,5 +655,9 @@ texture = ExtResource("5_ulevp")
|
|||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
||||||
shape = SubResource("RectangleShape2D_15sed")
|
shape = SubResource("RectangleShape2D_15sed")
|
||||||
|
|
||||||
|
[node name="Path2D" type="Path2D" parent="."]
|
||||||
|
|
||||||
|
[node name="PathFollow2D" type="PathFollow2D" parent="Path2D"]
|
||||||
|
|
||||||
[connection signal="mouse_entered" from="Area2D" to="." method="_markUnit"]
|
[connection signal="mouse_entered" from="Area2D" to="." method="_markUnit"]
|
||||||
[connection signal="mouse_exited" from="Area2D" to="." method="_unMarkUnit"]
|
[connection signal="mouse_exited" from="Area2D" to="." method="_unMarkUnit"]
|
||||||
|
|||||||
@@ -83,5 +83,6 @@ ZoomOut={
|
|||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
|
textures/canvas_textures/default_texture_filter=0
|
||||||
renderer/rendering_method="gl_compatibility"
|
renderer/rendering_method="gl_compatibility"
|
||||||
renderer/rendering_method.mobile="gl_compatibility"
|
renderer/rendering_method.mobile="gl_compatibility"
|
||||||
|
|||||||
93
unit.gd
Normal file
93
unit.gd
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
## Represents a unit on the game board.
|
||||||
|
## The board manages its position inside the game grid.
|
||||||
|
## The unit itself holds stats and a visual representation that moves smoothly in the game world.
|
||||||
|
@tool
|
||||||
|
class_name Unit_Move
|
||||||
|
extends Path2D
|
||||||
|
|
||||||
|
## Emitted when the unit reached the end of a path along which it was walking.
|
||||||
|
signal walk_finished
|
||||||
|
|
||||||
|
## Shared resource of type Grid, used to calculate map coordinates.
|
||||||
|
@export var grid: Resource
|
||||||
|
## Distance to which the unit can walk in cells.
|
||||||
|
@export var move_range := 6
|
||||||
|
## The unit's move speed when it's moving along a path.
|
||||||
|
@export var move_speed := 600.0
|
||||||
|
## Texture representing the unit.
|
||||||
|
@export var skin: Texture:
|
||||||
|
set(value):
|
||||||
|
skin = value
|
||||||
|
if not _sprite:
|
||||||
|
# This will resume execution after this node's _ready()
|
||||||
|
await ready
|
||||||
|
_sprite.texture = value
|
||||||
|
## Offset to apply to the `skin` sprite in pixels.
|
||||||
|
@export var skin_offset := Vector2.ZERO:
|
||||||
|
set(value):
|
||||||
|
skin_offset = value
|
||||||
|
if not _sprite:
|
||||||
|
await ready
|
||||||
|
_sprite.position = value
|
||||||
|
|
||||||
|
## Coordinates of the current cell the cursor moved to.
|
||||||
|
var cell := Vector2.ZERO:
|
||||||
|
set(value):
|
||||||
|
# When changing the cell's value, we don't want to allow coordinates outside
|
||||||
|
# the grid, so we clamp them
|
||||||
|
cell = grid.clamp(value)
|
||||||
|
## Toggles the "selected" animation on the unit.
|
||||||
|
var is_selected := false:
|
||||||
|
set(value):
|
||||||
|
is_selected = value
|
||||||
|
if is_selected:
|
||||||
|
_sprite.play("selected")
|
||||||
|
else:
|
||||||
|
_sprite.play("idle")
|
||||||
|
|
||||||
|
var _is_walking := false:
|
||||||
|
set(value):
|
||||||
|
_is_walking = value
|
||||||
|
set_process(_is_walking)
|
||||||
|
|
||||||
|
@onready var _sprite: AnimatedSprite2D = $PathFollow2D/AnimatedSprite2D
|
||||||
|
#@onready var _anim_player: AnimationPlayer = $AnimationPlayer
|
||||||
|
@onready var _path_follow: PathFollow2D = $PathFollow2D
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
set_process(false)
|
||||||
|
_path_follow.rotates = false
|
||||||
|
|
||||||
|
cell = grid.calculateGridCoordinates(position)
|
||||||
|
position = grid.calculateMapPosition(cell)
|
||||||
|
|
||||||
|
# We create the curve resource here because creating it in the editor prevents us from
|
||||||
|
# moving the unit.
|
||||||
|
if not Engine.is_editor_hint():
|
||||||
|
curve = Curve2D.new()
|
||||||
|
|
||||||
|
|
||||||
|
func _process(delta: float) -> void:
|
||||||
|
_path_follow.progress += move_speed * delta
|
||||||
|
|
||||||
|
if _path_follow.progress_ratio >= 1.0:
|
||||||
|
_is_walking = false
|
||||||
|
# Setting this value to 0.0 causes a Zero Length Interval error
|
||||||
|
_path_follow.progress = 0.00001
|
||||||
|
position = grid.calculateMapPosition(cell)
|
||||||
|
curve.clear_points()
|
||||||
|
emit_signal("walk_finished")
|
||||||
|
|
||||||
|
|
||||||
|
## Starts walking along the `path`.
|
||||||
|
## `path` is an array of grid coordinates that the function converts to map coordinates.
|
||||||
|
func walk_along(path: PackedVector2Array) -> void:
|
||||||
|
if path.is_empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
curve.add_point(Vector2.ZERO)
|
||||||
|
for point in path:
|
||||||
|
curve.add_point(grid.calculateMapPosition(point) - position)
|
||||||
|
cell = path[-1]
|
||||||
|
_is_walking = true
|
||||||
1
unit.gd.uid
Normal file
1
unit.gd.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c8ocnhejcdc77
|
||||||
Reference in New Issue
Block a user