找回密码
 注册帐号

扫一扫,访问微社区

士郎 用Unity重现《空洞骑士》的苦痛之路(2)——人物控制篇

11
回复
1976
查看
[ 复制链接 ]
排名
1
昨日变化

7599

主题

8145

帖子

3万

积分

Rank: 16

UID
1231
好友
186
蛮牛币
9987
威望
30
注册时间
2013-7-29
在线时间
3922 小时
最后登录
2019-4-23

活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

2019-4-1 09:56:00 显示全部楼层 阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本期将会挑选重点进行讲解,代码不会完全贴出(太长了),所以大部分方法都不是完整的,细节方面还请下载文章末尾的工程,打开查看。

本期相对于上篇难度巨幅提高,如果说上篇是normal那这篇就是very hard。食用时请注意别噎着。

移动核心逻辑(难)

为了让游戏中玩家的移动能够完全被开发者掌握,本工程并没有使用Unity的物理引擎来进行移动操作,而是手动模拟相应的物理特性并进行移动。

2D游戏中,玩家的移动总是能够分解成2个方向上的位移。我们移动的逻辑实现也是这样。

首先获取到这一帧的移动速度,然后计算出2个方向上的位移,再计算出下一个位置是否能够进行移动。如果无法移动,就需要对下一帧该方向的移动逻辑进行修正后再实施移动。原理如下图:

1.1.gif
移动原理

其中关于位置修正,我们还得手动打Box射线来检测是否碰撞,并根据结果进行相应的操作。原理如下:

1.2.gif
位移修正原理

请注意:由于我们是先移动X轴,在移动Y轴,并没有进行线性的移动,会在特定情况下舍弃部分位移(即位置出现偏差)。但是由于我们的移动是每一帧进行的,此处的误差实际上可以忽略,强迫症患者可以考虑在这个方法基础上进行修改。

代码实现需要获取到当前帧的速度,并且根据速度计算出这帧玩家给个方向的位移,用于下面的计算。然后根据移动的方向,分开处理。

先处理左右方向的位移,然后使用box射线检测,判断下一帧是否会碰到碰撞体或者陷阱,如果是墙壁(碰撞体),还需要通过碰撞点的位置进行修正,达到满意的移动效果。同时根据需求,更新对应的动画状态。代码如下:
[AppleScript] 纯文本查看 复制代码
public Vector3 moveSpeed;   //每一帧的移动速度
    public Vector2 boxSize;     //玩家碰撞盒的大小
    public void CheckNextMove()
    {
        Vector3 moveDistance = moveSpeed * Time.deltaTime;//当前帧的移动位移
        if (moveSpeed.x!= 0)//当左右速度有值时
        {
            RaycastHit2D lRHit2D = Physics2D.BoxCast(transform.position, boxSize, 0, Vector2.right * moveSpeed.x, 5.0f, playerLayerMask);
            if (lRHit2D.collider != null)//如果当前方向上有碰撞体
            {
                float tempXVaule = (float)Math.Round(lRHit2D.point.x, 1);                   //取X轴方向的数值,并保留1位小数精度。防止由于精度产生鬼畜行为
                Vector3 colliderPoint = new Vector3(tempXVaule, transform.position.y);      //重新构建射线的碰撞点
                float tempDistance = Vector3.Distance(colliderPoint, transform.position);   //计算玩家与碰撞点的位置
                if (tempDistance > (boxSize.x * 0.5f + distance))   //如果距离大于 碰撞盒子的高度的一半+最小地面距离
                {
                    transform.position += new Vector3(moveDistance.x, 0, 0); //说明此时还能进行正常移动,不需要进行修正
                }
                else//如果距离小于  根据方向进行位移修正
                {
                    float tempX = 0;//新的X轴的位置
                    if (moveSpeed.x> 0)
                    {
                        tempX = tempXVaule - boxSize.x * 0.5f - distance + 0.05f; //多加上0.05f的修正距离,防止出现由于精度问题产生的鬼畜行为
                    }
                    else
                    {
                        tempX = tempXVaule + boxSize.x * 0.5f + distance - 0.05f;
                    }
                    transform.position = new Vector3(tempX, transform.position.y, 0);//修改玩家的位置
                }
            }
            else
            {
                transform.position += new Vector3(moveDistance.x, 0, 0);
            }
        }
    }

同理,上下也是一样的。只不过需要考虑受到重力的情况。在不进行上下移动的时候,加上判断是否在地面的功能,用来决定是否添加重力。这可以通过朝地面打射线的方式进行判断。

代码如下:
[AppleScript] 纯文本查看 复制代码
public bool CheckIsGround()
    {
        RaycastHit2D hit2D = Physics2D.BoxCast(transform.position, boxSize, 0, Vector2.down,5f, playerLayerMask);
        if (hit2D.collider != null)
        {
            float tempDistance = Vector3.Distance(transform.position, hit2D.point);
            if (tempDistance > (boxSize.y * 0.5f + distance))//如果距离大于 碰撞盒子的高度的一半+最小地面距离
            {
                return false;
            }
            else
            {
                return true;
            }
        }
        else
        {
            return false;
        }
    }


左右移动

完成了上面主要移动核心逻辑、并在Update函数调用后,接下来的内容就要稍微简单一些。

由于实际的移动是由别的函数来进行实现,那么左右移动函数就只需要根据玩家的按键输入,来更新玩家的速度即可。如下:
[AppleScript] 纯文本查看 复制代码
public void LRMove()
    {
        float h = Input.GetAxis("Horizontal");
        moveSpeed.x = h * speed;//更新左右轴上的速度
        //接下来更新各种动画状态
    }


跳跃

在《空洞骑士》中,跳跃是根据按键的蓄力时长,来控制跳跃高度。如果我们需要实现这个功能,就需要在KeyDown事件中触发跳跃,key事件中进行蓄力,KeyUp事件中停止跳跃蓄力,跳跃状态取消,并更新动画。逻辑图如下:
1.jpg



跳跃功能逻辑图

根据上面的逻辑图实现的跳跃代码如下:
[AppleScript] 纯文本查看 复制代码
public float jumpTime;      //跳跃的最大蓄力时间
    float timeJump;             //跳跃当前的蓄力时间
    public void Jump()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            jumpState = true;  //进入跳跃状态
            moveSpeed.y += jumpPower;//初始添加向上的力
            timeJump = 0;//蓄力时间清零
        }
        else if (Input.GetKey(KeyCode.Space) && jumpCount<=2 && jumpState)
        {
            timeJump += Time.deltaTime;//蓄力时间增加
            if (timeJump < jumpTime)
            {
                moveSpeed.y += jumpPower;//蓄力
            }
        }
        else if (Input.GetKeyUp(KeyCode.Space))
        {
            jumpState = false;//退出跳跃状态
            timeJump = 0;//蓄力时间清零
        }
    }


二段跳实现的方法原理也是一样,只是需要在KeyDown事件中,区分当前是二段跳还是一段跳,并分别进行蓄力操作即可。

暗影冲刺

在冲刺状态下,玩家不受到重力,且此时不会接受按键输入。于是我们需要声明2个变量分别用来控制是否获取按键输入,以及应用重力。并在冲刺开始的时候关闭对应的开关,给玩家一个固定的移动速度,在冲刺结束后重新打开对应的开关,速度回滚。逻辑图如下:

1.jpg
暗影冲刺逻辑图

实现的代码如下:
[AppleScript] 纯文本查看 复制代码
public bool gravityEnable;  //重力开关
    public bool inputEnable;    //接受输入开关  true 游戏接受按键输入  false不接受按键输入
    public void SprintFunc()
    {
        if (Input.GetKeyDown(KeyCode.J) && isCanSprint)
        {
            StartCoroutine(SprintMove(sprintTime));//冲刺协程
        }
    }
    IEnumerator SprintMove(float time)
    {
        inputEnable = false;
        gravityEnable = false;//关闭按键输入,以及不在应用重力
        moveSpeed.y = 0;//Y轴速度清零
        isCanSprint = false;
        if (nowDir == PlayDir.Left)
        {
            moveSpeed.x = 15*-1;
        }
        else
        {
            moveSpeed.x = 15;
        }//根据方向施加速度
        yield return new WaitForSeconds(time);//延迟time秒后执行
        inputEnable = true;
        gravityEnable = true;
        isCanSprint = true;//状态回滚
    }


超级冲刺原理同上,但是需要考虑到需要蓄力,并且该状态结束是由按键与移动进行控制的。所以我们在实现超级冲刺的时候,需要用两种方式来结束。思路图如下:

1.jpg
超级冲刺逻辑图

代码就不贴出了,欢迎参考工程食用。

爬墙切换

原版游戏里,在空中时碰到特定墙壁会进入到爬墙的状态。

条件是下一帧需要碰到墙壁,且距离地面有一定的高度才行。代码如下:
[AppleScript] 纯文本查看 复制代码
public void EnterClimpFunc(Vector3 rayPoint) //移动检测到下一帧碰到墙壁时调用
    {
        //设定碰到墙 且  从碰撞点往下 玩家碰撞盒子高度内  没有碰撞体  就可进入碰撞状态。
        RaycastHit2D hit2D = Physics2D.BoxCast(rayPoint, boxSize, 0, Vector2.down, boxSize.y, playerLayerMask);
        if (hit2D.collider != null)
        {
            Debug.Log("无法进入爬墙状态  "+ hit2D.collider.name);
        }
        else
        {
            playAnimator.SetTrigger("IsClimb");//动画切换
            isClimb = true;
            isCanSprint = true; //爬墙状态,冲刺重置
        }
    }


有3种方式可以退出爬墙状态:

1.玩家自己受重力下落,超出墙壁的范围。

2.玩家按下跳跃键退出。

3.玩家按下反方向移动键退出。

这里只提一下跳跃退出实现的原理。比较简单,就不贴出代码了。在按下跳跃键后朝着墙壁的反方向施加一个朝上的力,就可使玩家离开碰撞体,退出爬墙状态。图示如下:

1.3.gif
跳跃退出实现原理图

攻击交互

在《空洞骑士》中,攻击碰到特定的物体时,会有后座力的效果,而且会刷新玩家身上特定的技能状态。实现逻辑就是在攻击的时候,进行射线检测,并根据碰撞体的标签,以及攻击的方向,来决定状态的刷新,以及是否施加后坐力的效果。逻辑图大致如下:

1.4.jpg
攻击判定逻辑图

在代码中的实现:
[AppleScript] 纯文本查看 复制代码
public void AttackFunc()
    {
        if (Input.GetKeyDown(KeyCode.K))//按下攻击键
        {
            CheckAckInteractive((int)nowDir);
        }
    }
    public void CheckAckInteractive(int dir)  //参数为攻击的方向
    {
        float distance = 1.8f;          //射线的检测长度
        RaycastHit2D hit2D = new RaycastHit2D();
        Vector2 raySize = new Vector2(boxSize.x + 0.5f, boxSize.y);         //扩大检测X轴范围
        switch (dir)
        {
            case 1:
                hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.left, distance, playerLayerMask);
                break;
            case 2:
                hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.right, distance, playerLayerMask);
                break;
            case 3:
                hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.up, distance, playerLayerMask);
                break;
            case 4:
                hit2D = Physics2D.BoxCast(transform.position, raySize, 0, Vector2.down, distance, playerLayerMask);
                break;
        }
        if (hit2D.collider!=null)
        {
            if (hit2D.collider.gameObject.CompareTag("Trap"))   //如果是陷阱就有后坐力
            {
                StartCoroutine(InteractiveMove(dir, 10));   //开启协程 施加后座力效果,并刷新状态
            }
        }
    }


结语

完成了本期文章的内容后,我们的主角在动作方面已经完全满足了这个项目的要求。

接下来需要完善的就是发挥自己的创意,搭建快乐地图啦。而且在本期文章中,考虑到有的童鞋可能动画切换这块的想法跟我不一致,我有意删减了动画切换相关的代码。若是需要参考的话,欢迎点击文章末尾的下载链接下载后进行查看。(PS:我使用的是2017.4.17f1版本)



工程下载链接


链接:https://pan.baidu.com/s/1wJTQmSup2EOOdGBYVmYvVw提取码:etv



知乎@繁华如梦
回复

使用道具 举报

6蛮牛粉丝
1280/1500
排名
2037
昨日变化

3

主题

164

帖子

1280

积分

Rank: 6Rank: 6Rank: 6

UID
126588
好友
0
蛮牛币
4546
威望
0
注册时间
2015-10-24
在线时间
439 小时
最后登录
2019-4-23
2019-4-1 11:00:12 显示全部楼层
感谢分享!!!!!!
回复

使用道具 举报

6蛮牛粉丝
1280/1500
排名
2037
昨日变化

3

主题

164

帖子

1280

积分

Rank: 6Rank: 6Rank: 6

UID
126588
好友
0
蛮牛币
4546
威望
0
注册时间
2015-10-24
在线时间
439 小时
最后登录
2019-4-23
2019-4-1 11:01:48 显示全部楼层
提取码是:etvn     百度到的,希望小编下次注意啦
回复 支持 反对

使用道具 举报

5熟悉之中
850/1000
排名
2761
昨日变化

0

主题

121

帖子

850

积分

Rank: 5Rank: 5

UID
8578
好友
0
蛮牛币
1413
威望
0
注册时间
2013-11-20
在线时间
201 小时
最后登录
2019-4-20
2019-4-1 14:00:01 显示全部楼层
感谢分享
回复

使用道具 举报

7日久生情
1843/5000
排名
1193
昨日变化

0

主题

541

帖子

1843

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
87577
好友
0
蛮牛币
6517
威望
0
注册时间
2015-3-31
在线时间
324 小时
最后登录
2019-4-23
2019-4-2 08:42:12 显示全部楼层
too good too strong!
回复 支持 反对

使用道具 举报

7日久生情
2142/5000
排名
2613
昨日变化

2

主题

1086

帖子

2142

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209046
好友
0
蛮牛币
2934
威望
0
注册时间
2017-3-30
在线时间
500 小时
最后登录
2019-4-23
2019-4-2 08:44:12 显示全部楼层
回复

使用道具 举报

7日久生情
1704/5000
排名
1990
昨日变化

5

主题

513

帖子

1704

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
54335
好友
2
蛮牛币
5526
威望
0
注册时间
2014-11-9
在线时间
504 小时
最后登录
2019-4-23
2019-4-2 14:07:08 显示全部楼层
提取码不对!!!
回复

使用道具 举报

0

主题

2

帖子

3

积分

Rank: 1

UID
318458
好友
0
蛮牛币
21
威望
0
注册时间
2019-4-2
在线时间
1 小时
最后登录
2019-4-3
2019-4-3 14:47:38 显示全部楼层
不错啊~~~~
回复

使用道具 举报

7日久生情
4919/5000
排名
1669
昨日变化

0

主题

3493

帖子

4919

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
185339
好友
1
蛮牛币
3569
威望
0
注册时间
2016-11-20
在线时间
648 小时
最后登录
2019-4-23
2019-4-4 10:08:14 显示全部楼层
9
回复

使用道具 举报

3偶尔光临
289/300
排名
9775
昨日变化

0

主题

43

帖子

289

积分

Rank: 3Rank: 3Rank: 3

UID
165870
好友
0
蛮牛币
222
威望
0
注册时间
2016-9-5
在线时间
132 小时
最后登录
2019-4-17
2019-4-8 13:32:01 显示全部楼层
感谢楼主无私分享
回复 支持 反对

使用道具 举报

4四处流浪
490/500
排名
5640
昨日变化

0

主题

41

帖子

490

积分

Rank: 4

UID
238078
好友
0
蛮牛币
827
威望
0
注册时间
2017-8-17
在线时间
199 小时
最后登录
2019-4-23
2019-4-8 15:15:06 显示全部楼层
第一期在哪儿找?
回复

使用道具 举报

7日久生情
1819/5000
排名
1923
昨日变化

0

主题

637

帖子

1819

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
35046
好友
0
蛮牛币
2
威望
0
注册时间
2014-7-18
在线时间
480 小时
最后登录
2019-4-22
前天 16:41 显示全部楼层
感谢分享
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则