23、实战平台跳跃游戏

第23章 实战:平台跳跃游戏

23.1 项目概述

构建一个完整的平台跳跃游戏,包含:

  • 角色移动、跳跃、冲刺、墙跳
  • 关卡设计(TileMap)
  • 收集品、存档点
  • 敌人和 Boss
  • UI(HUD、菜单)
  • 视差背景

23.2 项目结构

复制代码
scenes/
├── characters/
│   ├── player.tscn
│   └── enemies/
│       ├── slime.tscn
│       ├── bat.tscn
│       └── boss.tscn
├── levels/
│   ├── level_01.tscn
│   ├── level_02.tscn
│   └── boss_room.tscn
├── objects/
│   ├── coin.tscn
│   ├── checkpoint.tscn
│   ├── moving_platform.tscn
│   ├── spring.tscn
│   └── spike.tscn
└── ui/
    ├── hud.tscn
    ├── main_menu.tscn
    └── pause_menu.tscn

23.3 关卡设计

gdscript 复制代码
# level_01.tscn 节点结构
Level (Node2D)
├── Background (ParallaxBackground)
│   ├── Sky (ParallaxLayer)        motion_scale = (0, 0)
│   ├── Mountains (ParallaxLayer)  motion_scale = (0.2, 0.1)
│   └── Trees (ParallaxLayer)      motion_scale = (0.6, 0.3)
├── TileMapLayer (背景层)
├── TileMapLayer (地形层)           ← 碰撞在这里
├── TileMapLayer (装饰层)
├── TileMapLayer (前景层)
├── Objects
│   ├── Coins
│   ├── Checkpoints
│   ├── Spikes
│   └── MovingPlatforms
├── Enemies
│   ├── Slime1
│   ├── Slime2
│   └── Bat1
├── Player
└── UILayer (CanvasLayer)
    └── HUD

23.4 移动平台

gdscript 复制代码
extends AnimatableBody2D

@export var move_points: Array[Vector2] = []
@export var speed: float = 100.0
@export var wait_time: float = 1.0

var current_point: int = 0
var direction: int = 1
var is_waiting: bool = false

func _physics_process(delta: float) -> void:
    if is_waiting or move_points.size() < 2:
        return
    
    var target := move_points[current_point]
    var move_dir := (target - global_position).normalized()
    
    global_position = global_position.move_toward(target, speed * delta)
    
    if global_position.distance_to(target) < 1.0:
        global_position = target
        is_waiting = true
        await get_tree().create_timer(wait_time).timeout
        is_waiting = false
        
        current_point += direction
        if current_point >= move_points.size() or current_point < 0:
            direction *= -1
            current_point += direction * 2

23.5 陷阱设计

gdscript 复制代码
# 尖刺
extends Area2D

@export var damage: int = 100  # 一击必杀
@export var knockback_force: float = 500.0

func _ready() -> void:
    body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
    if body.is_in_group("player"):
        if body.has_method("take_damage"):
            var knockback_dir := Vector2.UP
            body.take_damage(damage, knockback_dir * knockback_force)

# 弹跳板
extends Area2D

@export var bounce_force: float = -800.0

func _ready() -> void:
    body_entered.connect(_on_body_entered)

func _on_body_entered(body: Node2D) -> void:
    if body is CharacterBody2D:
        body.velocity.y = bounce_force
        $AnimationPlayer.play("bounce")

23.6 收集品

gdscript 复制代码
extends Area2D

@export var coin_value: int = 1
@export var float_amplitude: float = 5.0
@export var float_speed: float = 2.0

var start_y: float

func _ready() -> void:
    start_y = position.y
    body_entered.connect(_on_body_entered)

func _process(delta: float) -> void:
    position.y = start_y + sin(Time.get_ticks_msec() * 0.001 * float_speed) * float_amplitude

func _on_body_entered(body: Node2D) -> void:
    if body.is_in_group("player"):
        GameManager.add_score(coin_value)
        
        # 收集动画
        var tween := create_tween()
        tween.tween_property(self, "position:y", position.y - 30, 0.2)
        tween.parallel().tween_property(self, "modulate:a", 0.0, 0.2)
        tween.tween_callback(queue_free)

23.7 Boss 战

gdscript 复制代码
extends CharacterBody2D

signal boss_defeated

@export var health: int = 100
@export var phase2_threshold: float = 0.5
@export var phase3_threshold: float = 0.25

var current_phase: int = 1
var attack_timer: float = 0.0
var attack_patterns: Array[Callable] = []

@onready var health_component: HealthComponent = $HealthComponent
@onready var anim: AnimatedSprite2D = $AnimatedSprite2D

func _ready() -> void:
    health_component.died.connect(_on_died)
    health_component.health_changed.connect(_on_health_changed)
    attack_patterns = [_attack_slam, _attack_projectile, _attack_charge]

func _on_health_changed(current: int, maximum: int) -> void:
    var ratio := float(current) / maximum
    if ratio <= phase3_threshold and current_phase < 3:
        enter_phase(3)
    elif ratio <= phase2_threshold and current_phase < 2:
        enter_phase(2)

func enter_phase(phase: int) -> void:
    current_phase = phase
    match phase:
        2:
            attack_timer = 0.0
            anim.modulate = Color(1, 0.8, 0.8)
        3:
            anim.modulate = Color(1, 0.3, 0.3)

func _attack_slam() -> void:
    anim.play("attack_slam")
    await anim.animation_finished
    # 地面冲击波
    spawnShockwave()

func _attack_projectile() -> void:
    anim.play("attack_projectile")
    for i in 3:
        spawn_projectile()
        await get_tree().create_timer(0.3).timeout

func _attack_charge() -> void:
    anim.play("charge")
    await anim.animation_finished

func _on_died() -> void:
    boss_defeated.emit()
    # 死亡动画
    modulate = Color(1, 1, 1, 1)
    var tween := create_tween()
    tween.tween_property(self, "modulate:a", 0.0, 1.0)
    await tween.finished
    queue_free()

23.8 关卡选择界面

gdscript 复制代码
extends Control

@onready var level_buttons: Array[Button] = []

func _ready() -> void:
    update_level_buttons()

func update_level_buttons() -> void:
    for i in level_buttons.size():
        var level_num := i + 1
        var is_unlocked := GameManager.is_level_unlocked(level_num)
        level_buttons[i].disabled = not is_unlocked
        level_buttons[i].text = "关卡 %d" % level_num
        
        if is_unlocked:
            var stars := GameManager.get_level_stars(level_num)
            level_buttons[i].text += "\n" + "★".repeat(stars) + "☆".repeat(3 - stars)

func _on_level_selected(level_num: int) -> void:
    GameManager.current_level = level_num
    get_tree().change_scene_to_file("res://scenes/levels/level_%02d.tscn" % level_num)
相关推荐
甲维斯6 小时前
0代码,0建模,3句话开发一个3D游戏!
前端·游戏·游戏开发
笨鸟先飞的橘猫8 小时前
树结构在游戏行业中的应用
学习·游戏
Sylvia33.8 小时前
火星数据体育API|一站式接入足球篮球电竞等18+项目实时数据
java·开发语言·python·websocket·游戏
淡海水13 小时前
11-02-Unity-Mono-vs-IL2CPP-两种脚本后端的数据结构行为差异
数据结构·unity·游戏引擎·il2cpp·mono
彧azz14 小时前
初学Unity:编辑器
笔记·学习·unity·游戏引擎
小小数媒成员15 小时前
GPU优化(1)
游戏·unity·游戏引擎
狂人开飞机16 小时前
27、实战物理益智游戏
游戏·游戏引擎·godot
一孤程1 天前
游戏测试专题第四篇:游戏性能测试实战-帧率/内存/发热全覆盖
游戏·测试