美文网首页
Godot基础教程04:实例-控制大炮

Godot基础教程04:实例-控制大炮

作者: 地球上的大海 | 来源:发表于2020-03-11 17:17 被阅读0次

    本节将讲解如何做一个简单的实例:

    • 用方向键左右控制大炮左右移动
    • 用方向键上下控制大炮炮口方向
    • 用空格键发射炮弹

    大概就是这个效果:


    运行效果

    嗯。。你是不是想说画面好丑。。由于本人画功清奇,大家先将就着看。。

    素材准备

    先准备以下素材:


    准备图片

    第一个是大炮的架子,我实在不会画,先整个方块代替一下。
    第三个是炮管,第四个是炮弹。

    场景布置

    新建一个项目,再新建一个2D场景,在2D场景中创建如下节点:


    main场景

    说明
    Node2D : 根节点,Node2D类型
    ----dapao : 大炮节点,Sprite类型,加载资源dapao.png
    --------paoguan : 炮管,Sprite类型,加载资源pao.png
    ------------zidanguadian : 子弹挂点,Node2D类型
    --------lunzi : 轮子,Sprite类型,加载资源lunzi.png

    细心的同鞋可能发现了,为什么没有炮弹节点呢?这是因为炮弹是在发射时动态创建出来的,而且场景中有可能会出现多个炮弹。
    按上面的说明,为每一个Sprite加载好资源,然后移动每个节点的位置,让它变成这样:

    场景摆放

    选中paoguan节点,在右侧属性面板展开offset,修改offset属性,使炮管的中心点处于根部。再在属性面板中找到Transform并展开,拖动Rotation Degrees右侧的滑块,即可旋转炮管。
    选中zidanguadian节点,在右侧属性面板中找到Transform并展开,修改x坐标,使这个节点的位置正好处于炮口处

    zidanguadian节点仅仅是用于定位,当发射炮弹时,只需要获得这个节点的坐标,作为炮弹的初始坐标即可
    当炮管旋转时,因为子弹挂点是炮管的子节点,所以其位置会受到炮管的影响,而始终处于炮口位置,这样就省去了计算炮口位置的麻烦

    保存这个场景,名为main.tscn
    新建一个场景,增加子节点如下:

    子弹场景

    Node2D节点下新建一个Sprite节点,并为其添加炮弹的图片资源,不要修改它的位置。
    将这个新场景保存为zidan.tscn

    主场景添加脚本

    这下重头戏来了,我们先在main.tscn场景中,为根节点添加一个脚本,名为main.gd,内容如下:

    extends Node2D
    
    #定义变量时在前面加 export,表示可以在编辑器中直接修改
    #大炮左右移动的速度 px/s
    export var speed = 100
    
    #炮管旋转速度 角度/s
    export var rotation_speed = 60
    
    #发射CD s
    export var cd = 0.3
    
    #炮管最高仰角
    export var max_rotation = 60
    
    #炮管最低仰角
    export var min_rotation = 0
    
    #加载zidan场景,并存到zidan变量中
    var zidan = preload("res://zidan.tscn")
    
    #为当前的cd计时
    var cd_timer = 0
    
    #每秒轮子需要转多少度
    var r
    
    #初始化
    func _ready():
        #计算每秒轮子需要转多少度
        #r放到这里计算而不是_process中,是因为这里计算结果不会改变
        #如果放到_process中,就会每帧计算重复的内容,影响运行效率
        r = speed / (150 * PI) * 360
    
    #每帧运行
    func _process(delta):
        # 定义变量move,当按下左键或右键时,分别为move赋不同的值
        var move = 0
        if Input.is_action_pressed("ui_right"):
            move += 1
        if Input.is_action_pressed("ui_left"):
            move -= 1
        #这里使用 move+=1 、 move-=1,而不是 move=1 、 move=-1
        #这是因为前者在同时按下左右键时,move的最终结果为0,而后者的move最终结果为-1
        
        # 如果move不为0,说明按下了左或右键
        if move != 0:
            #使用 $节点名称 来调用节点,$节点名称/节点名称 来调用多层子节点
            #position是位置属性,rotation_degrees是以角度为单位的旋转,rotation是以弧度为单位的旋转
            #delta是_process方法的参数,表示本帧距离上一帧间隔的时间,单位是秒
            #速度乘以delta,可以确保在不同的帧率下,物体的移动速度相同
            $dapao.position.x += speed * move * delta
            $dapao/lunzi.rotation_degrees += r * move * delta
        
        #当按下上键或下键时,改变炮管的方向
        if Input.is_action_pressed("ui_up"):
            $dapao/paoguan.rotation_degrees -= rotation_speed * delta
            #限制仰角最大值,这里为什么要用负数呢,因为负数是逆时针,和我们在游戏里要的效果相反
            if $dapao/paoguan.rotation_degrees < -max_rotation:
                $dapao/paoguan.rotation_degrees = -max_rotation
        if Input.is_action_pressed("ui_down"):
            $dapao/paoguan.rotation_degrees += rotation_speed * delta
            if $dapao/paoguan.rotation_degrees > min_rotation:
                $dapao/paoguan.rotation_degrees = min_rotation
        
        #记录cd时间
        cd_timer -= delta
        
        #当按下空格键时,发射子弹
        if Input.is_action_pressed("ui_select") and cd_timer < 0:
            #实例化一个zidan场景对象
            var zidanObj = zidan.instance()
            #设置这个子弹的位置,global_position为全局位置
            zidanObj.position = $dapao/paoguan/zidanguadian.global_position
            #使子弹的角度与炮管的当前角度相同
            zidanObj.rotation_degrees = $dapao/paoguan.rotation_degrees
            #把子弹添加进场景
            add_child(zidanObj)
            #设置cd时间
            cd_timer = cd
    

    定义变量时,在前面写上export,表示这个变量可以直接在编辑器中修改

    自定义属性

    我们来单独看这行代码:

    zidanObj.position = $dapao/paoguan/zidanguadian.global_position
    

    这里获得子弹挂点的全局坐标,但坐标值赋给了zidanObj的相对坐标。这是因为:

    • 子弹挂点zidanObj不是同一层级,因为我们是为根节点添加的脚本,所以zidanObj根节点的子节点
    • 全局坐标是相对于游戏场景左上角的,而相对坐标是相对于父节点的,这个时候大炮的位置、炮管的角度,都会影响到子弹挂点的全局坐标
    • zidanObj可以直接添加为子弹挂点的子节点吗?这样一来,不需要为zidanObj设置坐标,因为默认的(0,0)坐标就正好处于子弹挂点处。但这样有一个问题,就是当你移动炮管时,发射出去的炮弹会受到影响,因为它是炮管的子节点

    接下来我们讲解一下cd相关的代码,如果我们不看其它代码的话,cd相关的代码简略为:

    #这段代码是讲解cd功能是如何进行的,是简略代码,如果想复制实例代码,请复制上一段
    #……这里省略若干代码……
    
    #发射CD s,表示每隔0.3秒才能发射1个炮弹
    export var cd = 0.3
    #为当前的cd计时
    var cd_timer = 0
    
    #……这里省略若干代码……
    
    #每帧运行
    func _process(delta):
        #……这里省略若干代码……
        
        #记录cd时间
        cd_timer -= delta
        
        #当按下空格键时,发射子弹
        if Input.is_action_pressed("ui_select") and cd_timer < 0:
            #……这里省略创建子弹代码……
            #设置cd时间
            cd_timer = cd
    

    这里可以看到,我们定义了两个变量cdcd_timer
    _process方法第一次运行时,cd_timer由于减去了delta所以小于0,并且如果玩家一直不发射炮弹,cd_timer会越来越小。
    假如玩家在某一帧按下了空格键,此时开始创建子弹,并将cd_timer设为cd(本例中是0.3)。
    又到了下一帧,cd_timer减去了delta(假设帧率为25,那么delta平均是0.04),此时cd_timer的值是0.26,大于0,在进行空格键判断时,cd_timer<0判断无法通过,不能发射子弹。
    过了若干帧后,cd_timer的值终于小于0,此时才能继续发射。

    炮弹场景脚本

    现在为炮弹zidan.tscn场景的根节点添加脚本:

    extends Node2D
    
    #炮弹速度
    export var speed = 500
    
    func _process(delta):
        #炮弹向右飞
        #因为在本场景被添加进主场景时,会将本场景的根节点角度设置为炮管的角度
        #所以本场景的Sprite只需要改变x坐标,也能实现向不同角度发射的目的
        $Sprite.position.x += speed * delta
        
        #当发射出去1000px远时,删除自身以释放资源
        if $Sprite.position.x > 1000:
            self.get_parent().remove_child(self)
    

    好了,大功告成,接下来运行游戏试试看吧

    相关文章

      网友评论

          本文标题:Godot基础教程04:实例-控制大炮

          本文链接:https://www.haomeiwen.com/subject/dykhjhtx.html