笔记23 Unity项目:超级玛丽

  • 拖放地图,裁剪精灵,形成跑步动画,精灵渲染器
  • 摄像机跟随+代码CameraControl
  • 玛里奥动画
  • 给马里奥、地面等添加碰撞器、刚体(注意是2D的!)
  • 添加音乐播放器+代码AudioManager
  • 检测是否与地面碰撞(有两种方法)
  • 法一:利用射线检测是否碰到地面(适用此游戏)
  • 法二:不通过射线(不适用此游戏)
  • 敌人蘑菇+代码EnemyControl
  • 代码:马里奥PlayControl
  • 补充
  • 使用字符串(即动画名字)播放动画
  • 将名称(字符串)转成ID后使用
  • 静态金币+代码CoinStatic
  • 砖块+代码RockControl
  • 箱子、顶出动态金币+二者代码
  • 代码:箱子BoxControl
  • 代码:动态金币CoinControl
  • 顶箱子,顶出其他东西
  • 老师说要进行拓展练习(暂空)
  • 小记:《马里奥、〈白夜行〉、我》


拖放地图,裁剪精灵,形成跑步动画,精灵渲染器

unity第一人称移动_字符串

摄像机跟随+代码CameraControl

unity第一人称移动_unity第一人称移动_02

//跟随的物体
    public Transform target;

    //最小边界(能到的最左边)
    public float minX;
    //最大边界(能到的最右边)
    public float maxX;

	void Update () {
        //获取当前位置
        Vector3 pos = transform.position;
        //将它的x改成目标的x
        pos.x = target.position.x;
        //边界判断。需要传入两个边界
        if(pos.x > maxX)
        {
            pos.x = maxX;
        }
        if(pos.x < minX)
        {
            pos.x = minX;
        }
        //再赋值回去
        transform.position = pos;
	}

玛里奥动画

unity第一人称移动_unity第一人称移动_03

给马里奥、地面等添加碰撞器、刚体(注意是2D的!)

unity第一人称移动_加载_04

添加音乐播放器+代码AudioManager

unity第一人称移动_字符串_05

//音频播放器有了,首先写成单例类。
    public static AudioManager Instance;

    //写出两个播放器(背景音、音效)。然后到unity里关联(背景音,循环打钩)。
    public AudioSource bgmPlayer;
    public AudioSource sePlayer;

    void Awake()
    {
        //单例类一定要在Awake里面赋值。因为这个方法调用的时机比Start靠前。
        //这样可以保证在第一时间把这个单例做成。
        Instance = this;
    }

    // 下面给三个方法
    //一个播放bgm。传进来一个name,就是你播放的文件名称
    public void PlayMusic(string name)
    {
        //如果当前没有播放,再去播放
        if (bgmPlayer.isPlaying == false)
        {
            /*加载音频资源。类型AudioClip。
            从哪个位置加载?直接把name(外界传进来的)放那就可以了。*/
            AudioClip clip = Resources.Load<AudioClip>(name);
            //更换当前播放的声音片段
            bgmPlayer.clip = clip;
            //播放Play
            bgmPlayer.Play();
        }
    }
    //停止播放
    public void StopMusic()
    {
        bgmPlayer.Stop();
    }
    //播放音效
    public void PlaySound(string name)
    {
        /*加载音频资源。类型AudioClip。
        从哪个位置加载?直接把name(外界传进来的)放那就可以了。*/
        AudioClip clip = Resources.Load<AudioClip>(name);
        //播放音效
        sePlayer.PlayOneShot(clip);
    }

检测是否与地面碰撞(有两种方法)

法一:利用射线检测是否碰到地面(适用此游戏)

//利用射线检测是否碰到地面(法一)
        //画出射线。从中心点出发,向下*0.1米。
        Debug.DrawRay(transform.position, Vector2.down * 0.1f);
        /*创建2d射线。特点:可以直接返回。3d返回的是布尔值,hit在上面,利用out给hit值。
        而且在括号里可以直接填写射线的起点,方向,距离,检测的层(此处是第8层)。*/
        RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down ,0.1f,1<<8);
        if(hit.collider != null)
        {
            //说明碰到地面了
            isGround = true;
            //把跳跃的动画关闭
            ani.SetBool("Jump", false);
        }
        else
        {
            //没碰到地面(在空中)
            isGround = false;
            ani.SetBool("Jump", true);
        }

unity第一人称移动_unity第一人称移动_06

法二:不通过射线(不适用此游戏)

//判断是否碰撞到地面(法二)。此处不适用,比如碰到左边碰撞体,可以二段跳。
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.tag == "Ground")
        {
            isGround = true;
        }
    }
    private void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.collider.tag == "Ground")
        {
            isGround = false;
        }
    }

敌人蘑菇+代码EnemyControl

拖放,添加碰撞器、刚体,添加动画,编写代码,形成预设体。

unity第一人称移动_加载_07

//血量
    public int Hp = 1;
    //刚体
    private Rigidbody2D rBody;
    //动画
    private Animator ani;

    //保存方向。方向分为1(正方向)和-1(负方向)。
    private int dir = 1;

	void Start () {
        //在这里得到
        rBody = GetComponent<Rigidbody2D>();
        ani = GetComponent<Animator>();		
	}
	
	void Update () {
		if(Hp <= 0)
        {
            return;
        }
        /*移动(方向*速度)。
        为了使敌人碰到物体后向相反方向移动,此处乘以方向(上面设置了默认是正方向)。*/
        //老师写的是Vector3.left
        transform.Translate(Vector2.left * 0.2f * dir * Time.deltaTime);
	}
    //不管碰到什么物体,都向反方向移动
    private void OnCollisionEnter2D(Collision2D collision)
    {
        dir = -dir;
        //判断玩家碰到自己。注意给玩家标签。
        if(collision.collider.tag == "Player")
        {
            //contacts[0]碰撞的点,point代表拿到这一点的位置。
            //contacts[0].normal它还有一个法线,就是和碰撞点垂直的单位向量。
            //比如玩家从上面踩了蘑菇,会产生从碰撞点向下的法线。
            if (collision.contacts[0].normal == Vector2.down)
            {
                //也就是说玩家从上方踩到,敌人死亡
                //拿到玩家身上的刚体组件,给它添加一个向上的力。
                collision.collider.GetComponent<Rigidbody2D>().AddForce(Vector2.up * 100f);
                //死亡方法写在外面,此处调用。
                Die();
            }
            else
            {
                //玩家从其他方向碰到敌人,玩家死亡
                //拿到玩家的代码,调用里面的Die方法。
                collision.collider.GetComponent<PlayControl>().Die();
            }
        }
    }
    //死亡
    void Die()
    {
        Hp--;
        if(Hp <= 0)
        {
            //死亡
            //一秒之后销毁
            Destroy(gameObject, 1f);
            //动画
            ani.SetBool("Die", true);
            //声音
            AudioManager.Instance.PlaySound("踩敌人");
            /*碰撞器失效(因为死亡了,玩家踩到的时候不能再触发事件。
            而之所以能产生事件,是因为碰撞器的存在)*/
            GetComponent<Collider2D>().enabled = false;
            //删除刚体(碰撞器也可以用此方法。如果删除了碰撞器,而没有删除刚体,那么蘑菇会掉下去。)
            Destroy(GetComponent<Rigidbody2D>());
        }
    }

代码:马里奥PlayControl

//人物的血量
    public int Hp = 1;
    //是否在地面
    private bool isGround = false;
    //刚体
    private Rigidbody2D rBody;
    //动画
    private Animator ani;
    //获取精灵图片(为了后面翻转图片)
    private SpriteRenderer sr;


	void Start () {
        //在此处拿到这两个组件
        rBody = GetComponent<Rigidbody2D>();
        ani = GetComponent<Animator>();
        sr = GetComponent<SpriteRenderer>();
	}
	
	void Update () {
		if (Hp <= 0)
        {
            return;
        }
        //因为人物只是左右跑,所以拿水平轴
        float horizontal = Input.GetAxis("Horizontal");
        //如果水平轴不为0
        if (horizontal != 0)
        {
            //前进
            transform.Translate(transform.right * Time.deltaTime * 1 * horizontal);
            //翻转图片(模拟转头)
            if (horizontal > 0)
            {
                //说明是往右边走所以不翻转
                sr.flipX = false;
            }
            if (horizontal < 0)
            {
                sr.flipX = true;
            }
            //移动动画
            ani.SetBool("Run", true);
        }
        else
        {
            ani.SetBool("Run", false);
        }
        //跳跃。按了I键,并且人物是站在地上的。
        if (Input.GetKeyDown(KeyCode.I) && isGround == true)
        {
            //跳跃力(向上,作用在刚体上)
            rBody.AddForce(Vector2.up * 200);
            //播放跳的声音
            AudioManager.Instance.PlaySound("跳");
        }
       
        //利用射线检测是否碰到地面(法一)
        //画出射线。从中心点出发,向下*0.1米。
        Debug.DrawRay(transform.position, Vector2.down * 0.1f);
        /*创建2d射线。特点:可以直接返回。3d返回的是布尔值,hit在上面,利用out给hit值。
        而且在括号里可以直接填写射线的起点,方向,距离,检测的层(此处是第8层)。*/
        
       RaycastHit2D hit = Physics2D.Raycast(transform.position, Vector2.down, 0.1f, 1<<8);
       if(hit.collider != null)
       {
           //说明碰到地面了
           isGround = true;
           //把跳跃的动画关闭
           ani.SetBool("Jump", false);
       }
       else
       {
           //没碰到地面(在空中)
           isGround = false;
           ani.SetBool("Jump", true);
       }     
    }

    //玩家的死亡方法,因为在敌人的代码里调用,所以此处是public
    public void Die()
    {
        Hp--;
        if (Hp <= 0)
        {
            //死亡
            ani.SetTrigger("Die");
            //删除碰撞器。(因为玩家死亡时,是从场景中掉落的,所以只要删除碰撞器,不删刚体)。
            Destroy(GetComponent<Collider2D>());
            //停止播放背景音乐
            AudioManager.Instance.StopMusic();
            //播放死亡音乐
            AudioManager.Instance.PlaySound("死亡");
            //让玩家静止
            rBody.velocity = Vector2.zero;
            //让玩家静止,即将它的速度设为0。为了避免玩家在不同情况下死亡后,向上跳的高度不同)
            rBody.velocity = Vector2.zero;
            //跳跃。给一个向上的力
            rBody.AddForce(Vector2.up * 150f);
            //延时。1秒之后播放
            Invoke("Die2", 1f);
        }
    }
    void Die2()
    {
        AudioManager.Instance.PlaySound("死亡2");
    }

补充

使用字符串(即动画名字)播放动画

//
            //移动动画
            ani.SetBool("Run", true);

将名称(字符串)转成ID后使用

每个动画都有一个ID。
因为系统开辟字符串比开辟整形更加费性能,如果一个动作需要多次使用,优化方法:
在最开始的时候(Start里),把动作都转成int类型,用的时候,只要用int类型就好。

//移动动画
    int run = Animator.StringToHash("Run");
    //Debug.Log(run);打印ID
    ani.SetBool(run,true);
    
    //获取数值
    bool res = ani.GetBool(run);

静态金币+代码CoinStatic

unity第一人称移动_unity第一人称移动_08

//触发
    private void OnTriggerEnter2D(Collider2D collision)
    {
         if(collision.tag == "Player")
         {
            //播放声音
            AudioManager.Instance.PlaySound("金币");
            //销毁自己
            Destroy(gameObject);
         }
     }

砖块+代码RockControl

unity第一人称移动_字符串_09

//大砖块
    public GameObject Rock;
    //小砖块
    public GameObject[] Rocks;
    //写完上面的代码,需要到unity里面关联一下。

	// Use this for initialization
	void Start () {
		
	}
	

    private void OnCollisionEnter2D(Collision2D collision)
    {
        //如果玩家碰了我,并且碰撞点的法线向上(说明玩家是从线面碰到的)
        if (collision.collider.tag == "Player" && collision.contacts[0].normal == Vector2.up)
        {
            //播放音乐
            AudioManager.Instance.PlaySound("顶破砖");
            //小砖块会四散开来。首先遍历一下
            foreach(GameObject rock in Rocks)
            {
                //把父物体(大砖块)设置为空,小砖块就没有父物体了,会显示在前面
                rock.transform.parent = null;
                //3D有爆炸力,2D没有。首先,添加刚体。rock是4个小砖块
                Rigidbody2D rbody = rock.AddComponent<Rigidbody2D>();
                //得到从整体的中心点到各个小砖块中心点的向量。
                Vector2 dir = rock.transform.position - transform.position;
                //给力。dir沿着这个向量的方向*100的力
                rbody.AddForce(dir * 900f);
                //1秒之后销毁
                Destroy(rock, 1f);
            }
            //销毁大砖块
            Destroy(gameObject);
        }
    }

箱子、顶出动态金币+二者代码

unity第一人称移动_字符串_10

代码:箱子BoxControl

//血量。(顶几次)
    public int Hp = 5;
    //顶出来的物体
    public GameObject goPre;
    //音效名称(传进来)
    public string sound; 

	void Start () {
		
	}
	
	private void OnCollisionEnter2D(Collision2D collision)
    {
        //如果玩家从下面顶撞
        if (collision.collider.tag == "Player" && collision.contacts[0].normal == Vector2.up)
        {
            if(Hp > 0)
            {
                Hp--;
                //顶出物品
                //声音
                AudioManager.Instance.PlaySound(sound);
                //实例化物品
                Instantiate(goPre, transform.position, Quaternion.identity);
                if(Hp <= 0)
                {
                    //变成砖块(就是下面的“没了”)
                    //获得动画
                    GetComponent<Animator>().SetTrigger("Die");
                }
            }else
            {
                //箱子已经没了。
                //播放没了的声音
                AudioManager.Instance.PlaySound("顶砖石块");
            }
        }
    }

代码:动态金币CoinControl

void Start () {
        //获得刚体
        GetComponent<Rigidbody2D>().AddForce(Vector2.up * 150f);
        //销毁金币
        Destroy(gameObject, 1f);
    }

顶箱子,顶出其他东西

unity第一人称移动_加载_11