Godot-StartUP

创建于:2018-07-28

创建人: Justus

44 信息 140 成员
讨论基于Godot以及Unity引擎的游戏开发经验,理论和最佳实践。共享一些通用思路以启发另一种生产工具中的实践。独立开发群QQ: 122017359

在Godot中制作杀戮尖塔的箭头

logoss 2018-09-28

杀戮尖塔里面使用卡牌时的箭头是这样的:

Image title

箭头的形态非常符合贝塞尔曲线

PS中的钢笔工具就是用的贝塞尔曲线

Image title

如图,一条贝塞尔曲线需要用四个点来确定,一个起点,一个终点,加上两个控制点。

我们把四个点分别命名:起点(startPos),终点(endPos),控制点A(ctrlAPos),控制点B(ctrlBPos)

贝塞尔曲线的公式是:

position=startPos*(1-t)*(1-t)*(1-t)+3*ctrlAPos*t*(1-t)*(1-t)+3*ctrlBPos*t*t*(1-t)+endPos*t*t*t

四个点都确定后,公式里的t就是唯一的变量,t是指从起点到终点的百分比,取值是0-1。

比如0代表曲线起点,0.2代表曲线从起点开始20%的位置,0.5代表曲线中间位置,1代表曲线终点。

公式的计算结果position就是当前t值所对应的曲线上的点。


不过在游戏中我们是使用两个点来确定曲线的,卡牌所在位置是曲线的起点,鼠标所在位置是曲线的终点。

那么两个控制点就要根据起点和终点来进行计算。

杀戮尖塔里的曲线大致是这样:

Image title

我们可以大致的写出控制点的计算公式:

ctrlAPos.x=startPos.x+(startPos.x-endPos.x)*0.2
ctrlAPos.y=endPos.y-(endPos.y-startPos.y)*0.2

ctrlBPos.x=startPos.x-(startPos.x-endPos.x)*0.2
ctrlBPos.y=endPos.y+(endPos.y-startPos.y)*0.2

当然这个计算公式可以自己微调,使曲线更符合自己想要的形态


理解了曲线的原理,现在开始在godot中实现。

我画了两个箭头,箭头1和箭头2,如图。

Image titleImage title

在godot中新建一个场景,新建Node2D,命名为贝塞尔箭头。添加脚本。

Image title

开始写脚本。首先我们的箭头有20节,我们需要在初始化的时候准备好。

之后更新箭头时重新排好每一节就能形成一条曲线。

extends Node2D
var list=[] #数组,用来保存20节小箭头
func _ready():
    #生成19节尾巴小箭头,用箭头1的图片
    for i in range(19):
        var sprite=Sprite.new()    #新建Sprite节点
        add_child(sprite)          #添加到场景里
        list.append(sprite)        #添加到数组里
        sprite.texture=load("res://Sprites/箭头1.png")  #把图片换成箭头1
        sprite.scale=Vector2(1,1)*(0.2+float(i)/18*0.8) #改变缩放,根据杀戮尖塔,箭头是一节节越来越大的
        sprite.offset=Vector2(-25,0)  #由于我画的图片中心点在箭头中间,
                                      #这里改变一下图片偏移,把图片中心点移动到箭头头部
    #最后生成终点的箭头,用箭头2的图片
    var sprite=Sprite.new()   
    add_child(sprite)
    list.append(sprite)
    sprite.texture=load("res://Sprites/箭头2.png")
    sprite.offset=Vector2(-25,0)

然后我们需要一个函数来设置箭头的起点和终点

func reset(startPos,endPos):
    #根据传入的起点和终点来计算两个控制点
    var ctrlAPos=Vector2()
    var ctrlBPos=Vector2()
    ctrlAPos.x=startPos.x+(startPos.x-endPos.x)*0.1 #这里我把参数做了微调,感觉这样更加符合杀戮尖塔的效果
    ctrlAPos.y=endPos.y-(endPos.y-startPos.y)*0.2
    ctrlBPos.y=endPos.y+(endPos.y-startPos.y)*0.3
    ctrlBPos.x=startPos.x-(startPos.x-endPos.x)*0.3
   #根据贝塞尔曲线重新设置所有小箭头的位置
    for i in range(20):
        var t=float(i)/19 
        var pos=startPos*(1-t)*(1-t)*(1-t)+3*ctrlAPos*t*(1-t)*(1-t)+3*ctrlBPos*t*t*(1-t)+endPos*t*t*t
        list[i].position=pos 
   #虽然更改了箭头的位置,不过还需要重新计算箭头的方向   
   updateAngle()   #重新计算所有箭头的方向

接下来我们需要完成updateAngle()这个函数

思路是每个小箭头根据前一个箭头和自己的位置来计算角度

func updateAngle():
    for i in range(20):
        if i==0:
            list[0].rotation_degrees=270    #第一个小箭头就让他固定朝上好了
        else:
            var current=list[i]    #当前的小箭头
            var last=list[i-1]     #前一个小箭头
            var lenVec=current.position-last.position      #两个箭头连线的向量
            var a=lenVec.angle()            #计算这个向量的角度,这个angle()返回值是弧度
            a=rad2deg(a)               #弧度转成角度
           
            current.rotation_degrees=a   #更新小箭头的方向

我们的贝塞尔箭头就做好了,外界只要调用reset方法就能更新箭头,

不需要用的时候可以用visible=false把它隐藏掉

我把它放到游戏中看看效果

Image title

非常完美!





(转发自:原日志地址

近期喜欢的会员

 

加入 indienova

  • 建立个人/工作室档案
  • 建立开发中的游戏档案
  • 关注个人/工作室动态
  • 寻找合作伙伴共同开发
  • 寻求线上发行
  • 更多服务……
登录/注册