Unity经典游戏教程之:合金弹头

版权声明:

  • 本文原创发布于博客园"优梦创客"的博客空间(网址:http://www.cnblogs.com/raymondking123/)以及微信公众号“优梦创客”(微信号:unitymaker)
  • 您可以自由转载,但必须加入完整的版权声明!

场景搭建

  1. 将第一关的3张背景图片收尾相接

按住V键可以选中4个角进行拖拽和拼接
image
image
image

  1. 给它们一个共同的碰撞器作为地面
  2. 给场景中的石头添加碰撞器
  3. 给木头平台添加平台效果器

image

预制体的制作

  1. 木桶预制体

    1. 设置碰撞器,刚体设置为静态
    2. 添加脚本和动画控制器

    image
    3. 制作动画

    当木桶碰到子弹则修改木桶的sprite

    木桶有两滴血,当血量等于0的时候,播放闪烁的动画
    image

  2. 敌人预制体

    1. 设置碰撞器和刚体
    2. 添加脚本和动画控制器

    image
    3. 制作动画

    image

    敌人碰到子弹后会播放死亡动画

    敌人每隔5秒之后会向前移动一段距离,如果碰到玩家则会发起攻击,如果没有碰到则再过5秒之后继续移动

    敌人只有出现在Camera内的时候才会移动,否则不进行移动计时

  3. 老人预制体

    1. 设置碰撞器、触发器和刚体
    2. 添加脚本和动画控制器

    image
    3. 制作动画

    image
    image
    image
    image
    当老人在被绑住的时候碰到子弹,则被释放,播放释放动画

    当老人在被绑住的时候碰到玩家,玩家可以穿过它

    当老人被释放后碰到玩家,则会给玩家一个礼物,玩家吃掉礼物后会改变自身子弹的sprite

    当老人给完礼物后会穿过玩家跑走

  4. 敌机预制体

    1. 设置碰撞器和刚体
    2. 添加脚本和动画控制器

    image
    3. 制作动画

    image

    敌机在玩家走到某个位置时才会显示,并且追踪玩家的位置

    当与玩家的位置小于0.5时,会向下投放炸弹,若玩家被炸弹击中则游戏结束

    敌机有10滴血,若血量降到0,则播放爆炸动画,动画播放完成销毁敌机

  5. 子弹预制体

    1. 设置碰撞器和刚体
    2. 添加脚本和动画控制器

    image

    利用对象池,在游戏开始的时候就创建10个子弹,每当玩家发出子弹则改变对象池中的某个子弹的位置

    当子弹位置离开玩家5个单位以上则会重置到开始位置

    若子弹碰到碰撞体之后子弹会被重置

  6. 炸弹预制体

    1. 设置碰撞器和刚体
    2. 添加脚本和动画控制器

    image

Player的制作

  1. 将Body和Foot进行拼接,放在一个空节点下
  2. 创建一个空节点作为子弹的发出位置,并作为玩家的子节点
  3. 在Player脚下的碰撞器之外创建两个空节点作为射线检测的开始和结束位置用于检测是否落地

image

image
4. Player动画控制器的制作

image
5. Player移动的脚本实现

玩家的转身是通过旋转Y轴180°实现的

通过获取按键的数值,改变对象Y轴的旋转

Y轴的速度不能为0,不然跳不起来

Y轴的在空中或地上都要判断,而X轴的移动只能在地上才能取得

只有在地上的时候才能播放walk动画

跳跃的时候要将Walk的条件置为非,否则动画会在Jump和Walk之间来回切换

在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和

解决方法:将子弹出现的位置延后 并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染

当蹲下射击的时候子弹的位置为player的位置(player的位置比bulletLeftRight靠下一点,刚好满足下蹲发射降低高度的需求)

void FixedUpdate()
    {
        if (!GameControl.instance.isGameOver)
        {
            x = Input.GetAxis("Horizontal");
            y = Input.GetAxis("Vertical");
            absx = Mathf.Abs(x);

            ground = Physics2D.Linecast(hitlinestart.position, hitlineend.position, 1 << this.gameObject.layer);

            //通过获取按键的数值,改变对象Y轴的旋转,除去0 是为了让player保持之前一个方向
            if (x > 0 && ground)
                transform.eulerAngles = new Vector3(0, 180, 0);
            else if (x < 0 && ground)
                transform.eulerAngles = Vector3.zero;


            playerDir = transform.eulerAngles.y == 0 ? -1 : 1;//判断人物方向有没有改变,取得子弹的方向

            GetComponent<Rigidbody2D>().velocity = new Vector2(x * xspeed, GetComponent<Rigidbody2D>().velocity.y);//Y轴的速度不能为0,不然跳不起来

            GetComponent<Animator>().SetFloat("DirY", y);//Y轴的在空中或地上都要判断,而X轴的移动只能在地上才能取得

            if (ground)//只有在地上的时候才能播放walk动画
            {
                GetComponent<Animator>().SetFloat("DirX", absx);
                //GetComponent<Animator>().SetFloat("DirY", y);
                GetComponent<Animator>().SetBool("Jump", false);

                if (Input.GetKeyDown(KeyCode.K))//按了空格且在地上
                {
                    GetComponent<Rigidbody2D>().AddForce(new Vector2(0, yspeed));
                    GetComponent<Animator>().SetBool("Jump", true);
                    GetComponent<Animator>().SetFloat("DirX", 0);//跳跃的时候要将Walk的条件置为非,否则动画会在Jump和Walk之间来回切换
                }
            }


            if (Input.GetKeyDown(KeyCode.J) && ground)//播放射击动画的逻辑
            {
                GetComponent<Animator>().SetBool("Shoot", true); //在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和
                //解决方法:将子弹出现的位置延后  ,并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染

                bullets[bulletIndex].GetComponent<SpriteRenderer>().enabled = false;


                bullets[bulletIndex].transform.position = bulletLeftRight.position; //子弹的出现位置
                bullets[bulletIndex].transform.rotation = transform.rotation;//子弹的旋转跟人物的旋转一致
                bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(playerDir * bulletSpeed, 0);//默认子弹的朝向跟人物一致,Y轴速度为0

                if (Input.GetKey(KeyCode.W)) //按住上的同时按下J,向上射击
                {
                    bullets[bulletIndex].transform.position = transform.position + Vector3.up + playerDir * Vector3.right;//修改子弹出现的位置
                    bullets[bulletIndex].transform.eulerAngles = new Vector3(0, 0, -90f);//旋转子弹的角度
                    bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(0, bulletSpeed);//给子弹一个向上的速度
                }
                else if (Input.GetKey(KeyCode.S)) //按住下的同时按下J,蹲下射击
                {
                    bullets[bulletIndex].transform.position = transform.position;//子弹的位置为player的位置(player的位置比bulletLeftRight靠下一点,刚好满足下蹲发射降低高度的需求)
                }


                bulletIndex++;
                bulletIndex %= bullets.Length;
            }
            else if (!ground && Input.GetKeyDown(KeyCode.J) && Input.GetKey(KeyCode.W))//禁止玩家在空中左右射击
            {
                GetComponent<Animator>().SetBool("Shoot", true); //在下一帧才播放shoot动画,但子弹却在这一帧出现,有点违和
                //解决方法:将子弹出现的位置延后,并且将渲染关掉,当子弹的X轴位置大于人物X轴位置+1时,开启渲染

                bullets[bulletIndex].GetComponent<SpriteRenderer>().enabled = false;

                bullets[bulletIndex].transform.position = transform.position + Vector3.up + playerDir * Vector3.right;//修改子弹出现的位置
                bullets[bulletIndex].transform.eulerAngles = new Vector3(0, 0, -90f);//旋转子弹的角度
                bullets[bulletIndex].GetComponent<Rigidbody2D>().velocity = new Vector2(0, bulletSpeed);//给子弹一个向上的速度

                bulletIndex++;
                bulletIndex %= bullets.Length;
            }
            //将射击动画改为idel动画的条件,是子弹的位置跟人物位置距离超过5
            if (bulletIndex >= 1)
            {
                if (Mathf.Abs(bullets[bulletIndex - 1].transform.position.x - transform.position.x) > 2)
                    GetComponent<Animator>().SetBool("Shoot", false);
            }
            else
            {
                if (Mathf.Abs(bullets[bullets.Length - 1].transform.position.x - transform.position.x) > 2)
                    GetComponent<Animator>().SetBool("Shoot", false);
            }
        }
       

    }

Car的制作

  1. 将图片按照顺序进行拼接
  2. 将拼接好的车子放到一个空节点,给这个空节点一个刚体用于给它速度
    image
    image

GameOver和Win的显示

  1. 添加两个Text的UI组件,Text的内容分别为GameOver和You Win
  2. 在玩家死亡或者胜利的时候enable对应的Text
    image
    image

摄像机移动的脚本实现

  1. 将摄像机的X轴控制在它自身到35之内,并随着玩家位置改变(保证摄像机不能往回走)
 camerax = Mathf.Clamp(player.transform.position.x, camerax > 0 ? camerax : 0, 35.5f*level);

 playerx = Mathf.Clamp(player.transform.position.x, camerax - 8f, camerax + 8f);//将玩家位置控制在摄像机范围之内(保证玩家不能往回走)

 player.transform.position = new Vector3(playerx, player.transform.position.y, player.transform.position.z);

 mainCamera.transform.position = new Vector3(camerax, mainCamera.transform.position.y, -10);

开场动画的实现

  1. 通过时间来控制Car的移动和停止
  2. 通过MovePosition来让玩家有一个从车上跳出的效果,且在站定之后转向
    private void JumpOutOfCar()//开场动画
    {
        if (car.position.x >= -0.01)
        {
            player.SetActive(true);
            time += Time.fixedDeltaTime;
            if (time < 0.4f)
            {
                player.GetComponent<Rigidbody2D>().MovePosition(Vector2.MoveTowards(player.transform.position, new Vector2(-3f, 0f), 3.5f * Time.fixedDeltaTime));//人物跳起移动
            }
            else if (time > 0.4f && time < 1f)
            {
                player.GetComponent<Rigidbody2D>().MovePosition(Vector2.MoveTowards(player.transform.position, new Vector2(-4f, -1.65f), 3.5f * Time.fixedDeltaTime));//人物下落移动
            }
            else if (time > 1.5f)
            {
                player.transform.rotation = new Quaternion(0, 180, 0, 0);//人物转向
            }
        }

    }

敌机追踪玩家的脚本实现

  1. 判断玩家的位置是在Jet的左边还是右边,且向玩家位置移动
  2. 当与玩家位置小于0.5则扔炸弹
  3. 若血量为0 ,则在播放完成爆炸后销毁Jet
    void FixedUpdate()
    {
        trax = Mathf.Clamp(transform.position.x,29f,42f);
        transform.position = new Vector3(trax, transform.position.y, 0);

        if (GameControl.instance.mainCamera.transform.position.x > 35)//摄像机的位置控制直升机的出现
            GetComponent<SpriteRenderer>().enabled = true;
        if(GetComponent<SpriteRenderer>().enabled == true&&hp>0)
        {
            if(transform.position.x > GameControl.instance.player.transform.position.x+0.5)
            {
                transform.eulerAngles = new Vector3(0, 0, 0);
                GetComponent<Rigidbody2D>().velocity = 2*Vector2.left;
            }
            else if(transform.position.x < GameControl.instance.player.transform.position.x-0.5)
            {
                transform.eulerAngles = new Vector3(0, 180, 0);
                GetComponent<Rigidbody2D>().velocity = 2 * Vector2.right;
            }
            else
            {
                GetComponent<Rigidbody2D>().velocity = Vector2.zero;
                if(!boomAlive)
                {
                    Instantiate(jetBullet, transform.position + Vector3.down, Quaternion.identity);
                    boomAlive = true;
                }
            }
        }
        else
            GetComponent<Rigidbody2D>().velocity = Vector2.zero;

        if (hp == 0)
        {
            GetComponent<Animator>().SetBool("Explosion", true);
            Invoke("DestroyThis", 3f);
            Invoke("SetGameWin",3f);
        }


    }

Older给礼物和被释放的脚本实现

  1. 老人被释放后如果不持续施加速度,那么速度会因为摩擦力而衰减,最后导致速度为0
  2. 在没被释放之前,把older的碰撞器改为触发器,使玩家可以穿过older
  3. 在没被释放之前,玩家可以穿过older,玩家离开碰撞器后还把该碰撞器改回去
    void FixedUpdate()
    {
        if(isRelease)
        {
           
            time += Time.fixedDeltaTime;
            if (time >= 1f && time <=6f&&!isGiving)
            {
                GetComponent<Animator>().SetBool("run", true);
                GetComponent<Rigidbody2D>().velocity = new Vector2(-4f, GetComponent<Rigidbody2D>().velocity.y);
            }
            
            else if (time > 6)
                Destroy(this.gameObject);
        }
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Bullet")
        {
            isRelease = true;
            GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
            GetComponent<Animator>().SetBool("release", true);
        }
        if (collision.gameObject.tag == "Player" && !isGift&&isRelease)
        {
            isGift = true;//防止多次给礼物
            isGiving = true;//防止给礼物时还在移动
            GetComponent<Rigidbody2D>().velocity = Vector2.zero;
            GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
            collision.gameObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
            GetComponent<Animator>().SetBool("give", true);

            Invoke("GiveOver", 1f);
        }
        else if (collision.gameObject.tag == "Player"&& !isRelease)
        {//
            GetComponent<BoxCollider2D>().isTrigger = true;
        }
    }
    public void OnTriggerExit2D(Collider2D collision)
    {

        if (collision.gameObject.tag == "Player")
        {
            if (isRelease)
            {
                GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
                collision.gameObject.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
            }
            else
            {//
                GetComponent<BoxCollider2D>().isTrigger = false ;
            }
            
            
        }
    }

    private void GiveOver()
    {
        isGiving = false;
        GetComponent<Animator>().SetBool("give", false);
       
        Instantiate(gift, transform.position + Vector3.right, Quaternion.identity);
    }

更多知识、教程、源码请进↓↓↓
优梦创客工坊

精品内容推送请搜索↓↓↓
微信公众号:优梦创客

免费直播、VIP视频请进↓↓↓
优梦创客课堂

游戏开发交流群↓↓↓
游戏开发交流群

原文地址:https://www.cnblogs.com/raymondking123/p/8424566.html