Archived
1
0
Fork 0

Server-based collision and player death

This commit is contained in:
Sebastian Benjamin 2025-02-25 20:11:05 -08:00
parent 5d11565a4d
commit 0b8091b352
9 changed files with 96 additions and 26 deletions

View file

@ -30,6 +30,7 @@ unique_name_in_owner = true
scale = Vector2(6.75, 6.75)
[node name="Player" parent="World" instance=ExtResource("1_22cjd")]
unique_name_in_owner = true
[node name="NetworkManager" type="Node2D" parent="World" node_paths=PackedStringArray("player")]
script = ExtResource("2_b2dol")

View file

@ -0,0 +1,6 @@
class_name ScalableSprite2D
extends Sprite2D
func scale_sprite(world_space_multiple: float):
var scale_factor = (world_space_multiple * Globals.SERVER_SIZE.x) / self.texture.get_width()
self.scale = Vector2(scale_factor, scale_factor)

View file

@ -39,11 +39,20 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData):
match p_state.op_code:
2:
var data = JSON.parse_string(p_state.data)
print(data)
# Set player position given server bounds-checking
if data["forcePlayerPos"]:
player.position = Vector2(float(data["playerPos"]["x"]), float(data["playerPos"]["y"]))
player.set_position_data(
Vector2(
float(data["playerPos"]["x"]),
float(data["playerPos"]["y"])
),
float(data["playerPos"]["radius_multiplier"])
)
if int(data["deathTimer"]) > 0:
print("server says u died")
%Player.kill()
# Spawn new bullets
for b in data["newBullets"]:
@ -53,12 +62,16 @@ func _on_match_state(p_state : NakamaRTAPI.MatchData):
int(b["tick"]),
b["x"],
b["y"],
b["radius"],
b["radius_multiplier"] * Globals.SERVER_SIZE.x,
b["vel_x"],
b["vel_y"])
bullet.texture = load("res://test-bullet.png")
bullet.position = bullet.get_current_pos(int(b["tick"]))
bullet.scale = Vector2(0.2, 0.2)
# Reimplemented from ScalableSprite2D here atm
var scale_ratio = ((b["radius_multiplier"] * 2) * Globals.SERVER_SIZE.x) / bullet.texture.get_width()
bullet.scale = Vector2(scale_ratio, scale_ratio)
add_child(bullet)
bullets.append(bullet)
predicted_tick = int(b["tick"])

View file

@ -3,6 +3,17 @@ extends Node2D
@export var speed = 80
var velocity := Vector2.ZERO
var collision: DanmakuCircle = DanmakuCircle.new()
# This is temporary, it should be defined per-sprite when I get to the skin system
const PLAYER_BODY_WIDTH_MULTIPLIER = 0.18
# Temp
var flash_timer = 0.0
var flashing = false
func _ready() -> void:
$BodySprite.scale_sprite(PLAYER_BODY_WIDTH_MULTIPLIER)
func get_input():
if Input.is_action_pressed("Slow Mode"):
@ -13,10 +24,30 @@ func get_input():
velocity = Input.get_vector("Left", "Right", "Up", "Down") * speed
func _physics_process(delta: float):
# Temp
if flashing:
flash_timer -= delta
$BodySprite.modulate = Color(1, 1, 1, 1)
flashing = false
get_input()
# Bounds checking
var attempted_position := position + (velocity * delta)
attempted_position = attempted_position.clamp(Vector2(0, 0), Globals.SERVER_SIZE)
position = attempted_position
set_position_data(attempted_position, null)
func set_position_data(pos: Vector2, hurtcircle_scale_multiplier):
position = pos
collision.set_position(pos.x, pos.y)
if hurtcircle_scale_multiplier:
collision.set_radius(Globals.SERVER_SIZE.x*hurtcircle_scale_multiplier)
$HurtcircleSprite.scale_sprite(hurtcircle_scale_multiplier*2)
func kill():
# Temp
$BodySprite.modulate = Color(1, 0, 0, 1)
flash_timer = 0.5
flashing = true

View file

@ -1,11 +1,17 @@
[gd_scene load_steps=3 format=3 uid="uid://cd3tqt7hr5pqs"]
[gd_scene load_steps=5 format=3 uid="uid://cd3tqt7hr5pqs"]
[ext_resource type="Script" path="res://danmaku!/player.gd" id="1_r7xhp"]
[ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"]
[ext_resource type="Texture2D" uid="uid://c3deywcu4du2b" path="res://test-collision.png" id="3_gf44i"]
[ext_resource type="Script" path="res://danmaku!/ScalableSprite2D.gd" id="3_u0x7w"]
[node name="Player" type="Node2D"]
script = ExtResource("1_r7xhp")
[node name="Sprite2D" type="Sprite2D" parent="."]
scale = Vector2(0.09, 0.09)
[node name="BodySprite" type="Sprite2D" parent="."]
texture = ExtResource("2_04s0l")
script = ExtResource("3_u0x7w")
[node name="HurtcircleSprite" type="Sprite2D" parent="."]
texture = ExtResource("3_gf44i")
script = ExtResource("3_u0x7w")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
client/test-collision.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -47,7 +47,7 @@ pub extern "C" fn destroy_bullet(bullet: *mut Bullet) {
}
#[no_mangle]
pub extern "C" fn bullet_collides_with(tick: i64, bullet: *mut Bullet, c: *const Circle) -> bool {
pub extern "C" fn bullet_collides_with(bullet: *mut Bullet, tick: i64, c: *const Circle) -> bool {
if bullet.is_null() || c.is_null() {
return false;
}

View file

@ -32,9 +32,11 @@ const (
)
const (
STAGE_WIDTH float64 = 90.0
STAGE_HEIGHT float64 = 160.0
BULLET_KILL_BUFFER_WIDTH float64 = 16.0
STAGE_WIDTH float64 = 90.0
STAGE_HEIGHT float64 = 160.0
BULLET_KILL_BUFFER_WIDTH float64 = 16.0
PLAYER_COL_RADIUS_MULTIPLIER float64 = 0.04
PLAYER_DEATH_TIMER_MAX int = 180
)
// Interface for registering match handlers
@ -58,6 +60,7 @@ type GameTickUpdate struct {
PlayerPos map[string]interface{} `json:"playerPos"`
NewBullets []map[string]interface{} `json:"newBullets"`
ForcePlayerPos bool `json:"forcePlayerPos"`
DeathTimer int `json:"deathTimer"`
}
type PresenceState struct { // present time! hahahahahahahah!
@ -111,7 +114,7 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger
lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{
presence: presences[i],
stageState: PlayerStageState{
col: C.new_circle(C.double(STAGE_WIDTH*0.5), C.double(STAGE_HEIGHT-STAGE_HEIGHT*0.1), C.double(STAGE_WIDTH*0.09)),
col: C.new_circle(C.double(STAGE_WIDTH*0.5), C.double(STAGE_HEIGHT-STAGE_HEIGHT*0.1), C.double(STAGE_WIDTH*PLAYER_COL_RADIUS_MULTIPLIER)),
bullets: []*C.Bullet{},
updatePlayerPos: true,
health: 3,
@ -229,13 +232,21 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger
return false
})
// Check if the player collided with a bullet and kill them if so
deathTimer := 0
if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool {
return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.col))
}) {
deathTimer = PLAYER_DEATH_TIMER_MAX
}
var newBulletsToBroadcast = []map[string]interface{}{}
// Test bullet spawning
if tick%10 == 0 {
velx := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate)
vely := (rand.Float64() * STAGE_WIDTH) / float64(lobbyState.tickRate)
radius := 0.03
radius_multiplier := 0.03 + rand.Float64()*(0.1-0.03)
vel_x_sign := 2*rand.Intn(2) - 1
vel_y_sign := 2*rand.Intn(2) - 1
@ -244,7 +255,7 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger
C.int64_t(tick),
C.double(STAGE_WIDTH*rand.Float64()),
C.double(STAGE_HEIGHT*rand.Float64()),
C.double(radius),
C.double(radius_multiplier*STAGE_WIDTH),
C.double(float64(vel_x_sign)*velx),
C.double(float64(vel_y_sign)*vely),
)
@ -255,13 +266,13 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger
C.bullet_get_current_pos(bullet, C.int64_t(tick), &x, &y)
bulletData := map[string]interface{}{
"class": BULLET_LINEAR,
"tick": tick,
"x": float64(x),
"y": float64(y),
"radius": float64(radius),
"vel_x": float64(vel_x_sign) * velx,
"vel_y": float64(vel_y_sign) * vely,
"class": BULLET_LINEAR,
"tick": tick,
"x": float64(x),
"y": float64(y),
"radius_multiplier": float64(radius_multiplier),
"vel_x": float64(vel_x_sign) * velx,
"vel_y": float64(vel_y_sign) * vely,
}
newBulletsToBroadcast = append(newBulletsToBroadcast, bulletData)
@ -270,11 +281,13 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger
var tickData = GameTickUpdate{
Tick: tick,
PlayerPos: map[string]interface{}{
"x": v.stageState.col.x,
"y": v.stageState.col.y,
"x": v.stageState.col.x,
"y": v.stageState.col.y,
"radius_multiplier": PLAYER_COL_RADIUS_MULTIPLIER,
},
NewBullets: newBulletsToBroadcast,
ForcePlayerPos: v.stageState.updatePlayerPos,
DeathTimer: deathTimer,
}
v.stageState.updatePlayerPos = false