godot横板移动平台游戏学习笔记 2D战斗 基础移动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 extends Area2D @onready var icon = $Icon #移动速率 @export var speed = 100 func \_ready(): pass func \_process(delta): var direction = Vector2.ZERO if Input.is\_action\_pressed("move\_right"): direction.x = 1 if Input.is\_action\_pressed("move\_left"): direction.x = -1 if Input.is\_action\_pressed("move\_up"): direction.y = -1 if Input.is\_action\_pressed("move\_down"): direction.y = 1 position += direction \* delta \* speed
通过每帧获取当前节点二位向量位置,根据Input按键输入判断移动当当前节点位置
然后然后* delta是为了稳定帧率下的运行 *speed是为了灵活的调节移动速度
重力碰撞检测和动画播放 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 extends CharacterBody2D @onready var animation\_player = $AnimationPlayer @onready var sprite\_2d = $Sprite2D const GRAVITY = 2000.0 const WALK\_SPEED = 200 #跳跃高度 const JUMP\_FORCE = 750 #跳跃判断 var is\_jumping = false func \_physics\_process(delta): print(velocity.y) #物体和墙壁发生了碰撞 if is\_on\_wall(): print("撞墙") #没有上下的撞墙判断 只有一个力 if is\_on\_wall\_only(): print("空中撞墙撞墙") if velocity.y == -JUMP\_FORCE: print("jump") is\_jumping = false velocity.y += delta \* GRAVITY if Input.is\_action\_pressed("move\_left"): velocity.x = -WALK\_SPEED sprite\_2d.flip\_h = true animation\_player.play("walk") elif Input.is\_action\_pressed("move\_right"): velocity.x = WALK\_SPEED sprite\_2d.flip\_h = false animation\_player.play("walk") else: velocity.x = 0 move\_and\_slide() func \_process(delta): var direction = Input.get\_action\_strength("move\_right") - Input.get\_action\_strength("move\_left") if is\_jumping: #animation\_player.play("jump") pass elif direction == 0: animation\_player.play("idle") func \_input(event): if event.is\_action\_pressed("move\_up") and not is\_jumping and velocity.y == 0: velocity.y = -JUMP\_FORCE is\_jumping = true
使用move_and_slide()时,会检测碰撞并且滑动,从而实现了任意位置放置后,根据重力下落,然后进行移动
播放动画则是根据不同时机的判断完成
窗口尺寸问题 如果出现运行场景后,窗口的尺寸会随着放大缩小发生改变,通过修改项目设置—-窗口—–模式—–canvas_items来让他自适应
教学视频:https://www.bilibili.com/video/BV1Se411k7kS/?spm_id_from=333.337.search-card.all.click&vd_source=adbd70f3395ed4825cbab1afe7aa152e
实现变速移动 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 extends CharacterBody2D @onready var animation\_player = $AnimationPlayer @onready var sprite\_2d = $Sprite2D const GRAVITY = 2000.0 const WALK\_SPEED = 200 const JUMP\_FORCE = 750 var is\_jumping = false var walk\_acceleration = 0.0 var walk\_timer = 0.0 const WALK\_ACCELERATION\_TIME = 2.0 func \_physics\_process(delta): if is\_on\_wall(): print("撞墙") if is\_on\_wall\_only(): print("空中撞墙撞墙") if velocity.y == -JUMP\_FORCE: print("jump") is\_jumping = false velocity.y += delta \* GRAVITY if Input.is\_action\_pressed("move\_left"): velocity.x = -WALK\_SPEED sprite\_2d.flip\_h = true animation\_player.play("walk") elif Input.is\_action\_pressed("move\_right"): if walk\_timer >= WALK\_ACCELERATION\_TIME: walk\_acceleration = 2.0 # 例如:超过2秒后加倍加速度 else: walk\_acceleration = 1.0 # 正常加速度 velocity.x = WALK\_SPEED \* walk\_acceleration sprite\_2d.flip\_h = false animation\_player.play("walk") else: velocity.x = 0 walk\_acceleration = 0.0 move\_and\_slide() func \_process(delta): var direction = Input.get\_action\_strength("move\_right") - Input.get\_action\_strength("move\_left") if is\_jumping: # animation\_player.play("jump") pass elif direction == 0: animation\_player.play("idle") if Input.is\_action\_pressed("move\_right"): walk\_timer += delta else: walk\_timer = 0.0 func \_input(event): if event.is\_action\_pressed("move\_up") and not is\_jumping and velocity.y == 0: velocity.y = -JUMP\_FORCE is\_jumping = true if event.is\_action\_pressed("Sprint"): print("向右冲刺") velocity.x = -600
通过每帧判断,然后时间到了归0
LimboAI 使用这个插件来快速构建项目的状态机,状态机也可以依靠纯手写脚本构建,但是为了追求效率,目前先使用轮子,之后为了更加详细的了解状态机,会自己从头在造轮子。
使用LimboAI自带的函数可以快速创建各种状态,通过使用start 和 update 来控制状态之间的切换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 extends CharacterBody2D @onready var animation\_player = $AnimationPlayer @onready var sprite\_2d = $Sprite2D @onready var trail\_timer = $TrailTimer @onready var label = $"../Label" #状态机 var main\_sm: LimboHSM const GRAVITY = 2000.0 const WALK\_SPEED = 200 const JUMP\_FORCE = 750 var speed : int #当前状态 var state : String var is\_jumping = false var walk\_acceleration = 0.0 var walk\_timer = 0.0 const WALK\_ACCELERATION\_TIME = 0.5 func \_ready(): initate\_start\_machine() func \_physics\_process(delta): label.text = "speed:" + str(speed) + "\n" + "Player状态:" + str(main\_sm.get\_active\_state()) if is\_on\_wall(): print("撞墙") state = "落地后检测到碰撞" if is\_on\_wall\_only(): print("空中撞墙撞墙") state = "浮空下检测到碰撞" if velocity.y == -JUMP\_FORCE: print("jump") is\_jumping = false velocity.y += delta \* GRAVITY if Input.is\_action\_pressed("move\_left"): if walk\_timer >= WALK\_ACCELERATION\_TIME: walk\_acceleration = 2.0 # 例如:超过2秒后加倍加速度 else: walk\_acceleration = 1.0 # 正常加速度 speed = -WALK\_SPEED \* walk\_acceleration velocity.x = speed #print(-WALK\_SPEED \* walk\_acceleration) sprite\_2d.flip\_h = true animation\_player.play("walk") elif Input.is\_action\_pressed("move\_right"): if walk\_timer >= WALK\_ACCELERATION\_TIME: walk\_acceleration = 2.0 # 例如:超过2秒后加倍加速度 else: walk\_acceleration = 1.0 # 正常加速度 velocity.x = WALK\_SPEED \* walk\_acceleration #print(WALK\_SPEED \* walk\_acceleration) sprite\_2d.flip\_h = false animation\_player.play("walk") else: velocity.x = 0 walk\_acceleration = 0.0 move\_and\_slide() func \_process(delta): var direction = Input.get\_action\_strength("move\_right") - Input.get\_action\_strength("move\_left") if is\_jumping: # animation\_player.play("jump") pass elif direction == 0: animation\_player.play("idle") #通过帧率对加速度进行计时判断 if Input.is\_action\_pressed("move\_right") or Input.is\_action\_pressed("move\_left"): walk\_timer += delta else: walk\_timer = 0.0 #调用反转函数 flip\_sprite(direction) func \_input(event): if event.is\_action\_pressed("move\_up") and not is\_jumping and velocity.y == 0: velocity.y = -JUMP\_FORCE is\_jumping = true if event.is\_action\_pressed("Sprint"): print("冲刺") elif event.is\_action\_released("Sprint"): print("释放冲刺") func \_on\_trail\_timer\_timeout(): if velocity.x == 0: return var trail = preload("res://effects/trail.tscn").instantiate() get\_parent().add\_child(trail) get\_parent().move\_child(trail,get\_index()) var properties = [ "hframes", "vframes", "frame", "texture", "global\_position", "flip\_h", ] for name in properties: trail.set(name,sprite\_2d.get(name)) func flip\_sprite(dir): if dir == 1: #人物反转 #print("人物右转") pass elif dir == -1: #print("人物左转") pass func \_unhandled\_input(event): if event.is\_action\_pressed("move\_up"): main\_sm.dispatch(&"to\_jump") elif event.is\_action\_pressed("Sprint"): #冲刺 pass elif event.is\_action\_pressed("attack"): main\_sm.dispatch(&"to\_attack") func initate\_start\_machine(): #实例化状态机 main\_sm = LimboHSM.new() #添加状态机 add\_child(main\_sm) #创建idle状态 设置默认状态的启动方法 on\_enter 首次启动调用方法 update 更新后调用方法 var idle\_state = LimboState.new().named("idle").call\_on\_enter(idle\_start).call\_on\_update(idle\_update) #创建walk状态 var walk\_state = LimboState.new().named("walk").call\_on\_enter(walk\_start).call\_on\_update(walk\_update) #创建jump状态 设置默认状态的启动方法 on\_enter 首次启动调用方法 update 更新后调用方法 var jump\_state = LimboState.new().named("jump").call\_on\_enter(jump\_start).call\_on\_update(jump\_update) #创建walk状态 var attack\_state = LimboState.new().named("attack").call\_on\_enter(attack\_start).call\_on\_update(attack\_update) #加入状态机 main\_sm.add\_child(idle\_state) main\_sm.add\_child(walk\_state) main\_sm.add\_child(jump\_state) main\_sm.add\_child(attack\_state) #设置初始状态 main\_sm.initial\_state = idle\_state #状态机切换 #从闲置状态切换到行走状态 main\_sm.add\_transition(idle\_state,walk\_state,&"to\_walk") #从任意状态都可以回到闲置状态 这也符合游戏人物移动逻辑 main\_sm.add\_transition(main\_sm.ANYSTATE,idle\_state,&"state\_ended") #两个转换,公用一个调用方法 实现更灵活的调用 main\_sm.add\_transition(idle\_state,jump\_state,&"to\_jump") main\_sm.add\_transition(walk\_state,jump\_state,&"to\_jump") #任何状态都可以进行攻击的切换 main\_sm.add\_transition(main\_sm.ANYSTATE,attack\_state,&"to\_attack") #初始化状态机 main\_sm.initialize(self) #设置状态机激活状态 活动状态机 main\_sm.set\_active(true) #idle的首次启动调用方法 func idle\_start(): print("idle start") #可以播放闲置动画 #idle的每帧更新调用方法 func idle\_update(delta: float): if velocity.x != 0: main\_sm.dispatch(&"to\_walk") #walk的首次启动调用方法 func walk\_start(): print("角色行走中") #walk的每帧更新调用方法 func walk\_update(delta: float): if velocity.x == 0: main\_sm.dispatch(&"state\_ended") print("角色行走结束") #jump的首次启动调用方法 func jump\_start(): #调用动画播放Jump动画 #改变velocity.y = jump\_power 来实现跳跃 pass #jump的每帧更新调用方法 func jump\_update(delta: float): #判断跳跃是否落地 if is\_on\_floor(): #落地后切换闲置状态 main\_sm.dispatch(&"state\_ended") #attack的首次启动调用方法 func attack\_start(): #播放攻击动画 pass #attack的每帧更新调用方法 func attack\_update(delta: float): #当播放动画结束后,切换状态机回到闲置状态 #由于目前没有这个动画所以只用注释解释 后续可以根据情况修改 pass
视频教学:https://www.bilibili.com/video/BV1Y6421Z7sV/?spm_id_from=333.1007.top_right_bar_window_default_collection.content.click&vd_source=adbd70f3395ed4825cbab1afe7aa152e
里面的一些状态我并没有用上,因为之前我就是直接在脚本里判断写的移动,不好桥接,所以只是做了一些演示。实际代码都用注释去讲了。
但还是一个很好的学习项目
道具范围拾取检测 实现碰撞检测需要的关键节点是Area2D,它可以检测进入到区域的所有同类
这是我的Player场景构成
其中有两个CollisionShape2D,但作用完全不一样,Player下的Col节点是为了实现地面的碰撞,可以实现基础的移动,跳跃。
而Area2D,本质上不会达成碰撞,而是一个检测区域的作用。
这是我的道具场景构成:
可以看到,我使用Area2D的信号返回给了Boom节点,然后当Area2D的Coll检测到同类Arrea2D碰撞后,也就是进入这个区域内,会启动这个信号。
就这样,我们实现了基础的道具拾取检测,也可以用来完成其他的功能
Tween 使用Tween可以通过代码直接创建动画,无需创建节点
学习视频:https://www.bilibili.com/video/BV1Cy411z7xE/?spm_id_from=333.1007.tianma.1-1-1.click&vd_source=adbd70f3395ed4825cbab1afe7aa152e
场景切换 实现场景切换需要三个节点
CanvasLayer 父节点
ColorRect 子节点 用来进行切换的动画效果
AnimationPlayer子节点 用来进行切换的动画效果
首先用ColorRect制作一个黑幕动画,画面从透明变成黑色
父节点脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 extends CanvasLayer @onready var animation\_player = $AnimationPlayer func \_ready(): #隐藏自身 self.hide() func changer\_scene(path): self.show() self.set\_layer(999) animation\_player.play("changer") await animation\_player.animation\_finished get\_tree().change\_scene\_to\_file(path) animation\_player.play\_backwards("changer") await animation\_player.animation\_finished self.set\_layer(-1) self.hide() pass