欢迎光临散文网 会员登陆 & 注册

Unity快速上手系列之番外篇:《2D横版跑酷》

2018-09-06 18:42 作者:皮皮关做游戏  | 我要投稿

作者:四五二十


写在前面

未尝试做过跑酷游戏,大约相当于没学过游戏开发。

玩笑至此,想传达的意思大家想必也清楚。跑酷游戏普遍非常简单,但简单并不代表简陋,跑酷是最能体现“麻雀虽小五脏俱全”的游戏类型。在当中开发者可以充分展示自己的设计才能,无论是奇思妙想的关卡设计还是灵光一现的功能创意。这样必然能获取到充足而高频的正反馈——而这一切都是建立在这样一个门槛较低的游戏体量上的。

因此对于初学者来说,没有比它更合适练手的类型了,这也是我专门写这样一篇番外篇的目的。

闲话少叙,直接开干。


创建地形

地面

标配的地面和天花板自不必说,跟着自己的喜好设计即可。

而移动地形则能稍微增加一点变数。以垂直升降为例,在自己设定的移动范围内,地形最低位置就向上移动,反之亦然。把脚本挂载到对应地形上即可:

void Update()

        {

            if(transform.position.y > 0)

            {

                IsChange = false;

            }

            if(transform.position.y < -6)

            {

                IsChange = true;

            }

            if(IsChange == false)

            {

                //在世界坐标向下移动

                transform.Translate(0, -4 * Time.deltaTime, 0, Space.World);

            }

            else

            {

                //在世界坐标向上移动

                transform.Translate(0, 4 * Time.deltaTime, 0, Space.World);

            }

        }

 

水平移动同理。

背景

以下图为例,一张普通的砖墙背景:

为了展示出向前跑的效果,需要让图片每一帧向后移动。

transform.position = new Vector3(transform.position.x-0.03f, transform.position.y, transform.position.z);


  顺便说一句,在Unity2018版本中可以使用纹理偏移的方法来达到类似的效果,这样就不用单独移动图片了:

public float scrollSpeed = 0.5f;

        public Renderer = rend;

 

        //Use this for initialization

        void Start()

        {

            rend = GetComponent<Renderer>();

        }

 

        //Update is called once per frame

        void Update()

        {

            float offset = Time.time * scrollSpeed;

            rend.material.mainTextureOffset = new Vector2(offset, 0); //纹理偏移

        }

 

与之对应的就不再是创建一个精灵,而是创建Quad:

角色

角色图片

把图片素材放在精灵上,并加上刚体和碰撞盒子:

强调一下,网上的图片素材一定只能供自己学习,一定不能用作商业用途!


角色动作

1、移动

我们给角色一个速度,使得游戏一开始角色就向右移动。

myRigidbody.transform.Translate(speed * Time.deltaTime, 0, 0);

 

2、跳跃

按下空格键给它一个向上的力,使得角色可以向上跳跃。有两个判断:只有当检测到玩家在地面上,或者在天花板上时,才能跳跃。在空中不能跳跃。当然,通过稍微的逻辑修改,还可以让角色实现二段跳功能。


//按下空格键可以使方块跳跃

 if (Input.GetKeyDown(KeyCode.Space))

 {

     if (Physics2D.Raycast(transform.position, Vector2.down,hight, LayerMask.GetMask("ground")))

     {

         myRigidbody.AddForce(Vector3.up * upspeed, ForceMode2D.Impulse);    //给它一个向上的力

     }

     if (Physics2D.Raycast(transform.position, Vector2.down, hight, LayerMask.GetMask("ceiling")))

     {

         myRigidbody.AddForce(Vector3.up * upspeed, ForceMode2D.Impulse);    //给它一个向上的力

     }

 }

 

3、放缩

在这个示例里,我给予了玩家一个放缩的特性,通过灵活地改变体型的大小来通过障碍物。根据体型不同,跳跃能力、奔跑速度会发生相应的变化。

if (transform.localScale.x>0.3)

{

    //按下A键可以缩小方块

    if (Input.GetKey(KeyCode.A))

    {

        transform.localScale = new Vector3(transform.localScale.x - 0.01f, transform.localScale.y - 0.01f, transform.localScale.z - 0.01f);

        speed = speed + 0.05f;

        upspeed = upspeed + 0.05f;

    }

}

 


if(transform.localScale.x <=1)

{

    //按下D键可以变大方块

    if (Input.GetKey(KeyCode.D))

    {

        //检测到方块上面是天花板则不能变大

        if (Physics2D.Raycast(transform.position, Vector2.up, 0.5f*hight, LayerMask.GetMask("ceiling")))

        {

 

        }

        else

        {

            transform.localScale = new Vector3(transform.localScale.x + 0.01f, transform.localScale.y + 0.01f, transform.localScale.z + 0.01f);

            speed = speed - 0.05f;

            upspeed = upspeed - 0.05f;

        }

    }


效果如图:

摄像机

这一部分的处理较为简单,只需要让镜头跟随玩家移动即可。


//让相机跟着玩家移动

transform.position = new Vector3(player.transform.position.x + 5, 0 , transform.position.z);


金币

金币是场景中散落的收集要素,就跟《超级马里奥》中的金币一样。

图片资源随便用一个:

我们让金币带上一个旋转的样式:

transform.Rotate(Vector3.up * 4, Space.World);    //原地旋转

效果如下图所示:

根据收集逻辑,场景中的金币一旦被玩家所触碰到,就会自动移动至UI中金币数量位置。

金币的移动函数如下:


//金币向目标点移动

public void CoinMove()

{

    //UI坐标转换成世界坐标

    UIcoin = Camera.main.ScreenToWorldPoint(AllCoin.transform.position);

 

    //当前物体向这某一个物体移动

    transform.position = Vector3.MoveTowards(transform.position, UIcoin + Vector3.forward, 25 * Time.deltaTime);

 

    //两个坐标相减是方向,用sqrMagnitude获取方向的距离

    if ((transform.position - (UIcoin + Vector3.forward)).sqrMagnitude < 0.1f)

    {

        player.GetComponent<PlayerMove>().money++;

        player.GetComponent<PlayerMove>().SetMoney();

        Destroy(gameObject);

    }

}


当然,要触发这个移动函数,还需要增加一个检测碰撞的过程,在过程中调用移动函数。不要忘了给金币添加碰撞盒子。


void Update ()

{

    transform.Rotate(Vector3.up * 4, Space.World);    //原地旋转

 

    if (IsRun == true)

    {

        CoinMove();

    }

}

 

public void OnTriggerEnter2D(Collider2D coll)

{

    if (coll.gameObject.CompareTag("Player"))

    {

        IsRun = true;

    }

}


在角色脚本中获取该数量text。


void Start ()

{

    myRigidbody = this.GetComponent<Rigidbody2D>();

    money = 0;

    SetMoney();

    Hp.gameObject.SetActive(false);

}

 

public void SetMoney()      //改变金币数量

{

    MoneyText.text = money.ToString();

}

 

玩家碰到金币时,金币在移动至相应位置的同时,数量+1,并销毁自己。


player.GetComponent<PlayerMove>().money++;

player.GetComponent<PlayerMove>().SetMoney();

Destroy(gameObject);

 

机关

角色一旦碰到场景中的各种机关,就宣告game over。机关分为固定机关和移动机关两种。

移动机关当检测到角色靠近时,就自动向下掉落,碰到地板后停止运动,在掉落过程中如果碰到玩家则game over。

void Update ()

{

    if (isRun==true)

    {

        //玩家靠近绝对值距离小于4时火焰下落

        if (Mathf.Abs(player.transform.position.x-transform.position.x)<4)

        {

            transform.Translate(0, -9 * Time.deltaTime, 0, Space.World);

        }

    }

}

 

public void OnTriggerEnter2D(Collider2D coll)

{

    if (coll.gameObject.CompareTag("ground"))

    {

        isRun = false;

    }


磁铁功能

很多跑酷游戏里都会有“磁铁”这样的powerup道具,吃到后,会在一定时间内让场景中的金币自动被吸附到玩家身上。

碰撞盒

要实现这样的功能,我们首先需要给磁铁加一个可任意调整的碰撞盒。


随意改变一下形状,能碰到前方的金币即可。

拾取金币

public void OnTriggerEnter2D(Collider2D coll)

{

    if(coll.gameObject.CompareTag("coin"))

    {

        coll.GetComponent<Coin>().IsRun = true;

    }

}

 

碰撞盒碰到金币的盒子时,把金币的IsRun改为True,此时金币就会自动跑向目标点,达到磁铁吸金的效果。


跟随角色

角色碰到磁铁后,为了让吸金效果跟随角色,需要让磁铁本身也跟随角色一起移动。

if (coll.gameObject.CompareTag("Player"))

{

    IsFollow = true;

}

 

void Update ()

{

    if (IsFollow == true)

    {

        //跟着玩家移动

        transform.position = new Vector3(player.transform.position.x - 0.5f, player.transform.position.y + 1, transform.position.z);

    }

}

 

时间条

我们首先创建两张图片,分别为白色和绿色的,绿色图片在白色图片之上,两张大小一样。

找到面板上的绿色时间条,然后以帧为单位逐渐缩小尺寸。在减少为0的时候销毁磁铁和时间条,意味着磁铁失效。

//找到玩家

public GameObject player;

//找到血量条

public GameObject MagnetHp;

//找到HP图片

public Image HpPhoto;

//HP数值

public float Hp;

//是否移动

public bool IsFollow = false;

 

void Update ()

{

    if (IsFollow==true)

    {

        //改变Rotation

        transform.eulerAngles = new Vector3(0,0,0);

        transform.localScale = new Vector3(0.17f, 0.17f, transform.localScale.z);

        transform.position = new Vector3(player.transform.position.x-0.5f, player.transform.position.y+1, transform.position.z);

        player.GetComponent<PlayerMove>().Hp.gameObject.SetActive(true);

        Hp = Hp - 0.002f;

        if(Hp<0)

        {

            Hp = 0;

            Destroy(transform.parent.gameObject);     //销毁磁铁

            Destroy(MagnetHp);      //销毁血条

        }

 

        //实时更新面板上的HP数值

        HpPhoto.rectTransform.localScale = new Vector3(Hp, HpPhoto.rectTransform.localScale.y, HpPhoto.rectTransform.localScale.z);

    }

}

 

切换属性

这是在本篇示例中另外添加的一个特性:属性切换。

场景中有时会出现类似于水墙的障碍,玩家需要切换角色的属性后才能通过。

水属性图片:

当角色触碰到上图时,切换角色外形为如下:

player.GetComponent<SpriteRenderer>().sprite = tupian;


当检测到玩家属性已切换时,将水墙的碰撞盒变为可穿越即可。

rainbowwall.GetComponent<BoxCollider2D>().isTrigger = true;


游戏结束

goal标志

当玩家到达goal标志时,判定玩家闯关成功。

反之,玩家碰到机关、摔下悬崖或是未能穿过水墙时,判定游戏失败。


That's it。实际运行的效果如下:


完整的工程如下:https://github.com/wushupei/RunGame

这样一个简单的游戏却作用多多,不光可以应对类似于课程设计之类的学习任务,还足以作为开发者的入门第一次实践。希望能藉由此文,让尽量多的盆友们踏上游戏开发令人激动的旅程中去。


最后想系统学习游戏开发的童鞋,欢迎访问 http://levelpp.com/ 

游戏开发搅基QQ群:869551769 

微信公众号:皮皮关

Unity快速上手系列之番外篇:《2D横版跑酷》的评论 (共 条)

分享到微博请遵守国家法律