Initial commit
This commit is contained in:
commit
3b96451047
71 changed files with 2302 additions and 0 deletions
174
addons/walkabout/characters/character.gd
Normal file
174
addons/walkabout/characters/character.gd
Normal file
|
@ -0,0 +1,174 @@
|
|||
@tool
|
||||
@icon("character.svg")
|
||||
class_name WBCharacter
|
||||
extends CharacterBody2D
|
||||
## A character that can be controlled by events and behaviors and moves on a grid.
|
||||
|
||||
|
||||
## Emitted when the character begins moving.
|
||||
signal move_started()
|
||||
## Emitted when the character reaches its target position.
|
||||
signal move_finished()
|
||||
|
||||
|
||||
enum Dir {LEFT, RIGHT, UP, DOWN}
|
||||
|
||||
|
||||
const DIR_VECTORS: Dictionary[Dir, Vector2] = {
|
||||
Dir.LEFT: Vector2.LEFT,
|
||||
Dir.RIGHT: Vector2.RIGHT,
|
||||
Dir.UP: Vector2.UP,
|
||||
Dir.DOWN: Vector2.DOWN,
|
||||
}
|
||||
|
||||
|
||||
const DIR_ANIM_SUFFIXES: Dictionary[Dir, StringName] = {
|
||||
Dir.LEFT: &"_left",
|
||||
Dir.RIGHT: &"_right",
|
||||
Dir.UP: &"_up",
|
||||
Dir.DOWN: &"_down",
|
||||
}
|
||||
|
||||
|
||||
## Size of the grid the character is restricted to.
|
||||
@export var tile_size: float = 16.0
|
||||
## Speed the character walks at.
|
||||
@export var walk_speed: float = 4.0
|
||||
## Speed the character runs at.
|
||||
@export var run_speed: float = 8.0
|
||||
|
||||
## Direction the character is facing.
|
||||
@export var facing: Dir = Dir.DOWN
|
||||
|
||||
## Animation library for the character. [br]
|
||||
## At a minimum, the [code]idle_*[/code] animations are required. [br]
|
||||
## The following animations are used by default behavior: [br]
|
||||
## [code]idle_[left,right,up,down][/code] [br]
|
||||
## [code]walk_[left,right,up,down][/code] [br]
|
||||
## [code]run_[left,right,up,down][/code] [br]
|
||||
## [code]run_*[/code] will fallback to [code]walk_*[/code],
|
||||
## which will fallback to [code]idle_*[/code]. [br]
|
||||
## Addition custom animations may be provided to play on demand.
|
||||
@export var animations: SpriteFrames:
|
||||
set(value):
|
||||
animations = value
|
||||
sprite.sprite_frames = animations
|
||||
|
||||
## Texture drawing offset of the animated sprite.
|
||||
@export var sprite_offset: Vector2 = Vector2.ZERO:
|
||||
set(value):
|
||||
sprite_offset = value
|
||||
sprite.offset = sprite_offset
|
||||
|
||||
|
||||
## True when the character is moving.
|
||||
var moving: bool = false
|
||||
## Whether the character is running.
|
||||
var running: bool = false
|
||||
|
||||
## Tile position of the character on the grid.
|
||||
var tile_position: Vector2i:
|
||||
get():
|
||||
return pos_to_tile(global_position)
|
||||
|
||||
|
||||
var sprite: AnimatedSprite2D
|
||||
var _next_pos: Vector2
|
||||
var _playing_custom_animation: bool = false
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
sprite = AnimatedSprite2D.new()
|
||||
sprite.sprite_frames = animations
|
||||
add_child(sprite)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
global_position = closest_tile_center(global_position)
|
||||
for child in get_children():
|
||||
if child is CollisionShape2D or child is CollisionPolygon2D:
|
||||
return
|
||||
var col_shape := CollisionShape2D.new()
|
||||
col_shape.shape = RectangleShape2D.new()
|
||||
col_shape.shape.size = Vector2(tile_size - 2.0, tile_size - 2.0)
|
||||
add_child(col_shape)
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if moving:
|
||||
var move_delta := (run_speed if running else walk_speed) * tile_size * delta
|
||||
global_position = global_position.move_toward(_next_pos, move_delta)
|
||||
if global_position == _next_pos:
|
||||
moving = false
|
||||
move_finished.emit()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if moving:
|
||||
_playing_custom_animation = false
|
||||
var anims: Array[StringName] = [
|
||||
&"walk" + DIR_ANIM_SUFFIXES[facing],
|
||||
&"idle" + DIR_ANIM_SUFFIXES[facing]
|
||||
]
|
||||
if running:
|
||||
anims.push_front(&"run" + DIR_ANIM_SUFFIXES[facing])
|
||||
_try_animations(anims)
|
||||
elif not _playing_custom_animation:
|
||||
_try_animations([&"idle" + DIR_ANIM_SUFFIXES[facing]])
|
||||
|
||||
|
||||
## Makes the character move one tile in the given direction. [br]
|
||||
## If [param ignore_collision] is true, the character will not perform collision checks.
|
||||
func start_move(dir: Dir, ignore_collision: bool = false) -> bool:
|
||||
if moving:
|
||||
return false
|
||||
|
||||
facing = dir
|
||||
|
||||
_next_pos = global_position + DIR_VECTORS[dir] * tile_size
|
||||
var col := move_and_collide(_next_pos - global_position, true)
|
||||
if col and not ignore_collision:
|
||||
return false
|
||||
|
||||
moving = true
|
||||
move_started.emit()
|
||||
return true
|
||||
|
||||
|
||||
## Plays a given custom animation from the animation set. [br]
|
||||
## If [param reset_after] is [constant true], the animation will return to
|
||||
## the default idle animation after it finishes.
|
||||
func play_custom_animation(anim: StringName, reset_after: bool = false) -> void:
|
||||
_try_animations([anim])
|
||||
_playing_custom_animation = true
|
||||
|
||||
if reset_after and not animations.get_animation_loop(anim):
|
||||
await sprite.animation_finished
|
||||
_playing_custom_animation = false
|
||||
|
||||
## Stops playing custom animation if one is currently playing.
|
||||
func end_custom_animation() -> void:
|
||||
_playing_custom_animation = false
|
||||
|
||||
|
||||
## Returns the closest tile center position to a given position in global coordinates.
|
||||
func closest_tile_center(pos: Vector2) -> Vector2:
|
||||
var tile := pos - Vector2(tile_size, tile_size) * 0.5
|
||||
tile = tile.snappedf(tile_size)
|
||||
tile += Vector2(tile_size, tile_size) * 0.5
|
||||
return tile
|
||||
|
||||
## Returns the tile coordinates of a given position in global coordinates.
|
||||
func pos_to_tile(pos: Vector2) -> Vector2i:
|
||||
return Vector2i((global_position / Vector2(tile_size, tile_size)).floor())
|
||||
|
||||
## Returns the center position of a given tile in global coordinates.
|
||||
func tile_center_pos(tile: Vector2i) -> Vector2:
|
||||
return (Vector2(tile) * Vector2(tile_size, tile_size)) + (Vector2(tile_size, tile_size) * 0.5)
|
||||
|
||||
|
||||
func _try_animations(anims: Array[StringName]) -> void:
|
||||
for anim in anims:
|
||||
if animations.has_animation(anim):
|
||||
sprite.play(anim)
|
||||
return
|
Loading…
Add table
Add a link
Reference in a new issue