IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Godot游戏引擎学习 第六课

    1900发表于 2020-12-25 00:41:46
    love 0

    今天学的是如何添加怪物,并让怪物移动。

    这节课UP讲了好几个方面的东西,有点杂,我这里根据个人理解做点学习笔记。

    一、 怪物对象化

    怪物是有很多种类的,而且怪物具有很多相同过的特性和不同的特性。为了以后好开发将一些共有属性提取出来做个父类是很有必要的。

    这里UP就讲了一个继承脚本、场景的的东西,

    先建立一个Enemy场景,设定一些共有节点

    image-20201224210915823

    建立对应的脚本,独立出共有属性

    extends KinematicBody2D
    
    # 共有属性重力
    const gravity = 2000
    # 因为要处理精灵方向的问题,所以做个预载变量。
    onready var sprite: Sprite = $Sprite
    
    

    然后点击编辑器菜单上的场景按钮,选择建立继承场景,父场景选择Enemy.tscn,就会实例化出一个具有父场景所有属性的子场景,命名为Slime.tscn。然后在这个里面设置Slime的特有属性,如碰撞体、精灵等。

    脚本也同理,选中父节点,按右键选择打开脚本,UP住说这是一个翻译错误,弹幕里也有大佬说这里术语原文是 Extend,呼应脚本里的 extends 关键词,我的理解其实就是父类脚本。新建好的脚本如下,代码我都写了备注就不在这里说了:

    extends "res://src/enemies/Enemy.gd"
    
    # 定义枚举,用于指定怪物朝向
    enum Direction { Left = -1, Right = 1 }
    
    # 定义移动速度和加速度,这个其实可以提取出来到父脚本去
    const max_speed = 50
    const acceleration = max_speed / 2
    
    # 这里用到一个新东西:export变量,
    # 一个变量加上export就成为外部变量了,可以通过编辑器设置他的值
    # 括号里是指定这个变量的展示类型为上面设置的枚举
    export(Direction) var direction = Direction.Left
    # 速度
    var velocity = Vector2.ZERO
    
    func _physics_process(_delta):
    	# 每一个物理帧检查怪物是不是碰到墙了
    	var was_on_wall = is_on_wall()
    	# 怪物不会跳,所以把他们吸附在地板上
    	var snap = Vector2.DOWN * 16
    	# 进行移动操作
    	velocity = move_and_slide_with_snap(velocity, snap, Vector2.UP)
    
    	# 如果碰到墙就转身
    	if is_on_wall() and not was_on_wall:
    		direction *= -1
    
    
    func _process(delta):
    	# 进行加速移动
    	velocity.x = move_toward(velocity.x, max_speed * direction, acceleration * delta)
    	velocity.y += gravity * delta
    
    	# 进行转身
    	sprite.flip_h = velocity.x > 0
    	
    
    

    这里UP主还说了一个BUG,就是在更改了父场景的层归属后子场景不会更新,实例化出来的场景也不会更新,要把主场景关掉后重开就正常了。


    说到精灵这里插一下,有些精灵素材是整合在一起的,所以整个图片会很大,像下图这样,所以我们设置好精灵素材后,启用Region,然后可以先设置Scale把图片缩放到合适的大小,再设置v和hframes,开启网格吸附,将步长设置成每一帧图片的大小,再框选图片就好了。

    image-20201224211435566


    好了,建立好怪物之后就可以实例化到场景里UP又说了昨天碰撞层的问题,现在状态是怪物碰到玩家就不动了,他想让怪物穿过玩家,所以,我们再命名一个怪物层,把实例化的Slime的碰撞和遮罩层分配到这个里面。

    这样因为玩家和怪物不在一个层,所以碰不到怪物,但是怪物们再一个层,他们会互相碰撞。

    image-20201224213251529

    怪物笼

    其实就是怪物刷新点啦,让怪物定时刷新的功能。

    第六课 怪物笼

    我们可以利用Position2D的和Timer节点实现怪物笼效果,第一个用于定位怪物刷新出来的位置,第二个控制多常时间刷新。

    (这里说下,所有的信号时间必须在编辑器里注册,不然直接写进代码是没有效果的,我前几天说的那个可以手动写信号的话暂时先放一边,目前了解到的暂时不可以)

    代码如下:

    extends Position2D
    
    # 让外部可以设置打包场景
    export var enemy_scene: PackedScene
    # 刷新速度
    export var interval = 1.0
    
    #定时器
    onready var timer =$Timer
    
    func _ready():
    	# 如果设置了打包场景
    	if enemy_scene:
    		# 开始计时
    		timer.start(interval)
    
    # 每进行一次循环完毕的信号
    func _on_Timer_timeout():
    	# 根据设置的打包场景实例化新的怪物对象
    	var enemy: Node2D = enemy_scene.instance()
    	# 将实例化后的怪物添加到父节点中
    	get_parent().add_child(enemy)
    	#并将位置设置为Position的节点
    	enemy.global_position = global_position;
    

    信号的使用和攻击、受伤、攻击特效

    这里UP提出了攻击盒子和伤害盒子的概念,我因为不太理解的缘故中间代码和设置都整错了一部分,一直不太对,最后把添加的相关场景和代码全部删掉之后重写才正常实现。

    攻击盒子和伤害盒子和场景内角色和怪物都有的一个特性,所以为了复用代码就单独做了两个场景,Hitbox,Hurtbox,

    这里我看的时候理解混乱了好一会儿,后来算是明白了,其实一个攻击或受伤的步骤分为两步,比如攻击

    1. 我感觉到我打到你了
    2. 你感受到我打到你了,你叫了出来。

    其实理解后就很简单了,我的攻击碰到了你的受伤盒子,触发信号,我先执行攻击你的函数,再通知你受伤的信号,执行受伤的函数。

    这里制作了踩中怪物是的攻击特效和怪物的受伤特效

    # Player.gd > 省略前面的代码
    # 如果被怪物攻击了,怪物那边的攻击函数就会调用角色的受伤信号
    func _on_Hurtbox_hurt():
    	# 重新加载当前场景。
    	get_tree().reload_current_scene()
    
    # 如果踩中敌人了则自己处理自己的攻击过程,并通知对方受伤了。
    func _on_Hitbox_hit():
    	velocity.y = -jump_force / 2
    	
    # Enemy.gd > 省略了一部分代码
    # 编写信号链接函数
    func _on_Hurtbox_area_entered(hitbox):
        # 如果检测到碰着,也就是受伤
        # 首先调用受伤的信号
        emit_signal("hurt")
        # 再调用攻击信号
        hitbox.emit_signal("hit")
    

    大概就是这样把,感觉自己也没讲的很明白。

    总体代码如下:

    extends Area2D
    
    # 自定义攻击信号,方便实例后调用
    signal hit
    
    # 检测攻击状态
    func _on_Hitbox_area_entered(hurtbox):
        # 攻击到敌人先调用攻击信号
        emit_signal("hit")
        # 再调用对方的受伤信号
        hurtbox.emit_signal("hurt")
    
    extends Area2D
    
    # 自定义受伤信号,用于实例化调用
    signal hurt
    
    # 编写信号链接函数
    func _on_Hurtbox_area_entered(hitbox):
        # 如果检测到碰着,也就是受伤
        # 首先调用受伤的信号
        emit_signal("hurt")
        # 再调用攻击信号
        hitbox.emit_signal("hit")
    

    学习成果

    第六课 ALL



沪ICP备19023445号-2号
友情链接