ft (wip): tracking across boundaries

This commit is contained in:
2026-02-07 18:01:17 +01:00
parent 0f06846157
commit bc582efb90
12 changed files with 93 additions and 43 deletions

View File

@@ -60,10 +60,10 @@ func init_screen_size(x:float, y:float) -> void:
# This can take a vector of any size (but should be 2d, other components are unused) # This can take a vector of any size (but should be 2d, other components are unused)
func get_boundaried_position(position): func get_boundaried_position(position):
## clamp # clamp
#return position.clamp(Vector2.ZERO, screen_size) #return position.clamp(Vector2.ZERO, screen_size)
## periodic # periodic
position.x = wrapf(position.x, 0, screen_size.x) position.x = wrapf(position.x, 0, screen_size.x)
position.y = wrapf(position.y, 0, screen_size.y) position.y = wrapf(position.y, 0, screen_size.y)
return position return position
@@ -76,3 +76,39 @@ func get_new_flow():
flow_y = flow_mag * sin(flow_dir) flow_y = flow_mag * sin(flow_dir)
func calc_distance(one, two) -> float:
var candidate = one.distance_to(two)
var onedup = one
# FIXME: doesnt work-- predators lose track across game boundary
if one.x < screen_size.x/2:
if one.y < screen_size.y/2:
# top left
onedup.y -= screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y += screen_size.y
onedup.x -= screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
# bottom left
onedup.y += screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y -= screen_size.y
onedup.x -= screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
if one.y < screen_size.y/2:
# top right
onedup.y -= screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y += screen_size.y
onedup.x += screen_size.x
candidate = min(candidate, onedup.distance_to(two))
else:
# botom right
onedup.y += screen_size.y
candidate = min(candidate, onedup.distance_to(two))
onedup.y -= screen_size.y
onedup.x += screen_size.x
candidate = min(candidate, onedup.distance_to(two))
return candidate

View File

@@ -64,9 +64,10 @@ visible = false
scale = Vector2(0.385, 0.385) scale = Vector2(0.385, 0.385)
texture = ExtResource("2_68e2u") texture = ExtResource("2_68e2u")
[node name="WrappingManager" type="Node" parent="." unique_id=1406150436 node_paths=PackedStringArray("sprite")] [node name="WrappingManager" type="Node" parent="." unique_id=1406150436 node_paths=PackedStringArray("sprite", "shape")]
script = ExtResource("3_8lhj0") script = ExtResource("3_8lhj0")
sprite = NodePath("../AnimatedSprite2D") sprite = NodePath("../AnimatedSprite2D")
shape = NodePath("../CollisionShape2D")
metadata/_custom_type_script = "uid://bvbc0n0pslq7p" metadata/_custom_type_script = "uid://bvbc0n0pslq7p"
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1856148995] [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=1856148995]

View File

@@ -1,7 +1,6 @@
extends AbstractPredator2D extends AbstractPredator2D
# FIXME: (general) tracking across wrapping boundary # FIXME: (general) tracking across wrapping boundary
# TODO: mirroring (thought, extracct that to general function/resource?
var can_attack: bool = true var can_attack: bool = true
var desired_rotation: float = self.rotation var desired_rotation: float = self.rotation

View File

@@ -100,23 +100,24 @@ script = ExtResource("1_xp037")
maxHealth = 50 maxHealth = 50
metadata/_custom_type_script = "uid://dgfimmq53whll" metadata/_custom_type_script = "uid://dgfimmq53whll"
[node name="WrappingManager" type="Node" parent="." unique_id=826586678 node_paths=PackedStringArray("sprite")] [node name="WrappingManager" type="Node" parent="." unique_id=826586678 node_paths=PackedStringArray("sprite", "shape")]
script = ExtResource("9_shhro") script = ExtResource("9_shhro")
sprite = NodePath("../AnimatedSprite2D") sprite = NodePath("../AnimatedSprite2D")
shape = NodePath("../CollisionPolygon2D")
metadata/_custom_type_script = "uid://bvbc0n0pslq7p" metadata/_custom_type_script = "uid://bvbc0n0pslq7p"
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." unique_id=1596156928]
light_mask = 4
visibility_layer = 4
position = Vector2(0.111679085, 1.1167793)
rotation = -1.5707964
polygon = PackedVector2Array(-22.184862, -27.994831, 23.481365, -27.21198, 13.82622, 25.891317, -6.005971, 25.891317)
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=410999609] [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." unique_id=410999609]
rotation = 4.712389 rotation = 4.712389
sprite_frames = SubResource("SpriteFrames_shhro") sprite_frames = SubResource("SpriteFrames_shhro")
animation = &"Hunting" animation = &"Hunting"
[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="." unique_id=1596156928]
light_mask = 4
visibility_layer = 4
position = Vector2(0.11167908, 1.1167793)
rotation = -1.5707964
polygon = PackedVector2Array(-22.184862, -27.994831, 23.481365, -27.21198, 13.82622, 25.891317, -6.005971, 25.891317)
[node name="StateMachine" type="Node" parent="." unique_id=1857729810 node_paths=PackedStringArray("initial_state")] [node name="StateMachine" type="Node" parent="." unique_id=1857729810 node_paths=PackedStringArray("initial_state")]
script = ExtResource("3_xp037") script = ExtResource("3_xp037")
initial_state = NodePath("RandomMovement") initial_state = NodePath("RandomMovement")

View File

@@ -11,12 +11,12 @@ func enter(previous_state_path: String, data := {}) -> void:
finished.emit(owner.fsm.States.IDLE, {}) finished.emit(owner.fsm.States.IDLE, {})
func physics_update(_delta: float) -> void: func physics_update(_delta: float) -> void:
if target == owner or target == null or owner.position.distance_to(target.position) > owner.sight_range: if target == null or GameManager.calc_distance(owner.position, target.position) > owner.sight_range:
finished.emit(owner.fsm.States.IDLE, {}) finished.emit(owner.fsm.States.IDLE, {})
return return
# alternatively, we could use a collision shape and inbuilt signals, but im not sure if that works better (mixing signals and custom fsm stuff i mean # alternatively, we could use a collision shape and inbuilt signals, but im not sure if that works better (mixing signals and custom fsm stuff i mean
if owner.position.distance_to(target.position) > owner.attack_range: if GameManager.calc_distance(owner.position, target.position) > owner.attack_range:
owner.move(move_towards(target.position)) owner.move(move_towards(target.position))
else: else:
owner.attack(target) owner.attack(target)

View File

@@ -58,9 +58,10 @@ script = ExtResource("2_0227s")
speed = 0.5 speed = 0.5
maxHealth = 20 maxHealth = 20
[node name="WrappingManager" type="Node" parent="." index="0" unique_id=407460873 node_paths=PackedStringArray("sprite")] [node name="WrappingManager" type="Node" parent="." index="0" unique_id=407460873 node_paths=PackedStringArray("sprite", "shape")]
script = ExtResource("3_lecx4") script = ExtResource("3_lecx4")
sprite = NodePath("../AnimatedSprite2D") sprite = NodePath("../AnimatedSprite2D")
shape = NodePath("../CollisionPolygon2D")
metadata/_custom_type_script = "uid://bvbc0n0pslq7p" metadata/_custom_type_script = "uid://bvbc0n0pslq7p"
[node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." index="1" unique_id=788182944] [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="." index="1" unique_id=788182944]

View File

@@ -11,7 +11,7 @@ func enter(previous_state_path: String, data := {}) -> void:
finished.emit(owner.fsm.States.IDLE, {}) finished.emit(owner.fsm.States.IDLE, {})
func physics_update(_delta: float) -> void: func physics_update(_delta: float) -> void:
if owner.position.distance_to(threat.position) > threshold or threat == owner: if GameManager.calc_distance(owner.position, threat.position) > threshold:
finished.emit(owner.fsm.States.IDLE, {}) finished.emit(owner.fsm.States.IDLE, {})
return return
owner.move(flee_from(threat.position)) owner.move(flee_from(threat.position))

View File

@@ -1,3 +1,4 @@
@abstract
extends CharacterBody2D extends CharacterBody2D
class_name NPC2D class_name NPC2D

View File

@@ -1,3 +1,4 @@
@abstract
extends NPC2D extends NPC2D
class_name AbstractPredator2D class_name AbstractPredator2D

View File

@@ -1,3 +1,4 @@
@abstract
extends NPC2D extends NPC2D
class_name AbstractPrey2D class_name AbstractPrey2D

View File

@@ -1,3 +1,4 @@
@abstract
class_name StateMachine extends Node class_name StateMachine extends Node
@export var initial_state: State = null @export var initial_state: State = null

View File

@@ -1,30 +1,38 @@
class_name WrappingManager extends Node class_name WrappingManager extends Node
@export var sprite: AnimatedSprite2D @export var sprite: AnimatedSprite2D
@export var shape: Node # FIXME (also in refactor see below) this is bad.
# Mirrored sprites for periodic boundary # Mirrored sprites for periodic boundary
var mirrorSprite1: Node2D var mirrors: Array
var mirrorSprite2: Node2D
var mirrorSprite3: Node2D
# 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:
await owner.ready await owner.ready
mirrorSprite1 = sprite.duplicate()
mirrorSprite2 = sprite.duplicate()
mirrorSprite3 = sprite.duplicate()
owner.add_child(mirrorSprite1) mirrors.append(Area2D.new())
owner.add_child(mirrorSprite2) mirrors.append(Area2D.new())
owner.add_child(mirrorSprite3) mirrors.append(Area2D.new())
# TODO: npc overhaul; make npc2d (and child classes) inherit from Node instead of Area2d.
# Each entity should have Area2d -> collisionshape2d + animatedsprite2d. We then duplicate this area2d instead of the bullshit thats happening here
# note taht the below bs also does not work, as the (freshly instantiated) area2ds have none of the signals connected. The above refactor will fix this.
for m in mirrors:
for i in owner.get_groups():
if not str(i).begins_with("_"):
m.add_to_group(i)
m.set_collision_layer(owner.get_collision_layer())
m.set_collision_mask(owner.get_collision_mask())
m.add_child(sprite.duplicate())
m.add_child(shape.duplicate())
owner.add_child(m)
_handle_wrapping() _handle_wrapping()
func play_sprite(anim: String) -> void: func play_sprite(anim: String) -> void:
mirrorSprite1.play(anim) mirrors[0].get_node("AnimatedSprite2D").play(anim)
mirrorSprite2.play(anim) mirrors[1].get_node("AnimatedSprite2D").play(anim)
mirrorSprite3.play(anim) mirrors[2].get_node("AnimatedSprite2D").play(anim)
func _process(delta: float) -> void: func _process(delta: float) -> void:
_handle_wrapping() _handle_wrapping()
@@ -49,34 +57,34 @@ func _handle_wrapping():
# Find corresponding section of the screen # Find corresponding section of the screen
if owner.position.x < GameManager.screen_size.x/2 and owner.position.y < GameManager.screen_size.y/2: if owner.position.x < GameManager.screen_size.x/2 and owner.position.y < GameManager.screen_size.y/2:
# Right # Right
mirrorSprite1.global_position = owner.global_position + Vector2(GameManager.screen_size.x, 0) mirrors[0].global_position = owner.global_position + Vector2(GameManager.screen_size.x, 0)
# Diag # Diag
mirrorSprite3.global_position = owner.global_position + Vector2(GameManager.screen_size.x, GameManager.screen_size.y) mirrors[2].global_position = owner.global_position + Vector2(GameManager.screen_size.x, GameManager.screen_size.y)
# Bottom # Bottom
mirrorSprite2.global_position = owner.global_position + Vector2(0, GameManager.screen_size.y) mirrors[1].global_position = owner.global_position + Vector2(0, GameManager.screen_size.y)
elif owner.position.x < GameManager.screen_size.x/2: elif owner.position.x < GameManager.screen_size.x/2:
# Top # Top
mirrorSprite1.global_position = owner.global_position + Vector2(0, - GameManager.screen_size.y) mirrors[0].global_position = owner.global_position + Vector2(0, - GameManager.screen_size.y)
# Diag # Diag
mirrorSprite2.global_position = owner.global_position + Vector2(GameManager.screen_size.x, - GameManager.screen_size.y) mirrors[1].global_position = owner.global_position + Vector2(GameManager.screen_size.x, - GameManager.screen_size.y)
# Right # Right
mirrorSprite3.global_position = owner.global_position + Vector2(GameManager.screen_size.x, 0) mirrors[2].global_position = owner.global_position + Vector2(GameManager.screen_size.x, 0)
elif owner.position.y < GameManager.screen_size.y/2: elif owner.position.y < GameManager.screen_size.y/2:
# Left # Left
mirrorSprite1.global_position = owner.global_position + Vector2(- GameManager.screen_size.x, 0) mirrors[0].global_position = owner.global_position + Vector2(- GameManager.screen_size.x, 0)
# Bottom # Bottom
mirrorSprite2.global_position = owner.global_position + Vector2(0, GameManager.screen_size.y) mirrors[1].global_position = owner.global_position + Vector2(0, GameManager.screen_size.y)
# Diag # Diag
mirrorSprite3.global_position = owner.global_position + Vector2(- GameManager.screen_size.x, GameManager.screen_size.y) mirrors[2].global_position = owner.global_position + Vector2(- GameManager.screen_size.x, GameManager.screen_size.y)
else: else:
# Left # Left
mirrorSprite1.global_position = owner.global_position + Vector2(- GameManager.screen_size.x, 0) mirrors[0].global_position = owner.global_position + Vector2(- GameManager.screen_size.x, 0)
# Diag # Diag
mirrorSprite2.global_position = owner.global_position + Vector2(- GameManager.screen_size.x, - GameManager.screen_size.y) mirrors[1].global_position = owner.global_position + Vector2(- GameManager.screen_size.x, - GameManager.screen_size.y)
# Top # Top
mirrorSprite3.global_position = owner.global_position + Vector2(0, - GameManager.screen_size.y) mirrors[2].global_position = owner.global_position + Vector2(0, - GameManager.screen_size.y)