圈数限制

为了驱动玩家向下一个圆圈跳跃,所以我们限制圆圈上转的圈数,圈数结束后游戏结束

Circle添加一个Label节点

选择合适字体及文字大小,并使其内容以及布局均为居中

circle.gd中编写脚本

enum MODES {STATIC, LIMITED} # 两种状态的圆圈
var mode = MODES.STATIC
var num_orbits = 3     # 圈数计数
var current_orbits = 0 # 完成圈数
var orbit_start = null # 轨道起始位置

设置圆圈的模式

func set_mode(_mode):
    mode = _mode
    match mode:
        MODES.STATIC:
            $Label.hide()
        MODES.LIMITED:
            current_orbits = num_orbits
            $Label.text = str(orbits_left)
            $Label.show()

初始化圆圈模式

func init(_position, _radius=radius, _mode=MODES.LIMITED):
    set_mode(_mode)

删除jumper.gd中的这句

target.get_node("Pivot").rotation = (position - target.position).angle()

circle.gd

func capture(target):
    jumper=target
    $AnimationPlayer.play("capture")
    $Pivot.rotation = (jumper.position-position).angle() 
    orbit_start = $Pivot.rotation

Main.gd中传入参数

func _on_Jumper_captured(object):
    $Camera2D.position = object.position
    object.capture(player)
    call_deferred("spawn_circle")

circle.gd

func _process(delta):
    $Pivot.rotation += rotation_speed * delta
    if mode == MODES.LIMITED and jumper:
        check_orbits()

func check_orbits():
    # 检测是否经过一圈
    if abs($Pivot.rotation - orbit_start) > 2 * PI:
        current_orbits -= 1
        $Label.text = str(current_orbits)
        if orbits_left <= 0:
            jumper.die()
            jumper = null
            implode()
        orbit_start = $Pivot.rotation

Jumper下的VisibilityNotifier2D节点添加screen_eited()信号

当玩家死亡、不在轨道上且跳出屏幕

func die():
    target = null
    queue_free()

func _on_VisibilityNotifier2D_screen_exited():
    if !target:
        die()

测试1

当圈数为0时玩家和圆圈都会消失

圈数效果

给圈数计数添加一个雷达扫描的效果

关于2d绘图的官方文档https://docs.godotengine.org/zh_CN/stable/tutorials/2d/custom_drawing_in_2d.html

circle.gd

func _draw():
    if jumper:
        var r = ((radius - 50) / num_orbits) * (1 + num_orbits - current_orbits)
        draw_circle_arc_poly(Vector2.ZERO, r, orbit_start + PI/2,$Pivot.rotation + PI/2, Color(1, 0, 0))
    
func draw_circle_arc_poly(center, radius, angle_from, angle_to, color):
    var nb_points = 32
    var points_arc = PoolVector2Array()
    points_arc.push_back(center)
    var colors = PoolColorArray([color])

    for i in range(nb_points + 1):
        var angle_point = angle_from + i * (angle_to - angle_from) / nb_points - PI/2
        points_arc.push_back(center + Vector2(cos(angle_point), sin(angle_point)) * radius)
    draw_polygon(points_arc, colors)

然后在_process()中调用update()

func _process(delta):
    $Pivot.rotation += rotation_speed * delta
    if mode == MODES.LIMITED and jumper:
        check_orbits()
        update()

测试2

菜单

场景框架

新建一个场景BaseScreen.tscn,这是一个基本的UI框架,保存在UI文件夹中,节点如下

CanvasLayer (BaseScreen)

  • MarginContainer

    • VBoxContainer

      • Label
      • HBoxContainer (Buttons)
      • HBoxContainer (Buttons2)
  • Tween

MarginContainer

布局选择整个矩形

适当修改外边距,让其距离四周有一定距离

BaseScreenoffset x修改为500

然后新建BaseScreen,gd脚本,为场景添加动画

extends CanvasLayer
onready var tween = $Tween
func appear():
    tween.interpolate_property(self, "offset:x", 500, 0,0.5, Tween.TRANS_BACK, Tween.EASE_IN_OUT)
    tween.start()
func disappear():
    tween.interpolate_property(self, "offset:x", 0, 500,0.4, Tween.TRANS_BACK, Tween.EASE_IN_OUT)
    tween.start()

开始场景

完成基本框架后,场景->新建继承场景,选择刚才的BaseScreen场景

新建一个TitleScreen场景,在Buttons下添加TextureButton

把按钮添加到buttons组中

这时修改初始场景的属性便可修改该场景的子场景

修改VBoxContainerSeparation属性让控件间距增大

设置场景

同上,新建SettingsScreen场景,并添加按钮

结束场景

同上,新建SettingsScreen场景,并添加按钮

场景管理

新建Screens场景,三个UI场景为Node的子节点

**

添加Screens.gd脚本

extends Node
# 开始游戏的信号
signal start_game
# 当前的UI界面
var current_screen = null
func _ready():
    register_buttons()
    change_screen($TitleScreen)
# 注册按钮
func register_buttons():
    # 获取buttons组
    var buttons = get_tree().get_nodes_in_group("buttons")
    # 按钮连接信号
    for button in buttons:
        button.connect("pressed", self, "_on_button_pressed", [button.name])

func _on_button_pressed(name):
    match name:
        "Return","Home":
            change_screen($TitleScreen)
        "Play":
            change_screen(null)
            yield(get_tree().create_timer(0.5), "timeout")
            emit_signal("start_game")
        "Settings":
            change_screen($SettingsScreen)
# 改变UI界面
func change_screen(new_screen):
    if current_screen:
        current_screen.disappear()
        yield(current_screen.tween, "tween_completed")
    current_screen = new_screen
    if new_screen:
        current_screen.appear()
        yield(current_screen.tween, "tween_completed")

func game_over():
    change_screen($GameOverScreen)

回到Main场景,将Screens添加到Main中,并为其连接信号

并在Main.gd中的_ready()函数中删去new_game()

为玩家失败添加信号

# 死亡信号
signal died
func die():
    emit_signal("died")
    target = null
    queue_free()

Main.gd中连接信号并将Circle添加到circles的分组中

func new_game():
    # 连接信号 
    player.connect("captured", self, "_on_Jumper_captured")
    player.connect("died",self,"_on_Jumper_died")
func _on_Jumper_died():
    get_tree().call_group("circles", "implode")
    $Screens.game_over()

测试3

Last modification:August 17th, 2020 at 12:14 am