天天看点

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

MySuperMario----2D复刻之旅,缅怀童年时光

超级玛丽/超级马里奥

你好!欢迎浏览我的博客!超级玛丽是一款非常经典的2D游戏,今天我将代领大家学习如何从小白开始,用unity一步一步复刻这一经典。

一、开发工具准备

  1. unity2017以上版本 ,我制作的时候用的unity2018.1.0版本;

    下载链接: unity发行版本.

  2. 任何支持c#编程语言的集成开发环境 ,我使用的是visual studio2015;
  3. 搭建游戏的素材 ,这里我直接将做好的素材分享给大家,其中包括 游戏背景地图,相关人物设置,背景音乐 等。不过有兴趣的同学可以去爱给网上下载自己喜欢的素材,然后搭建自己喜欢的游戏素材;

    素材链接:https://pan.baidu.com/s/16Kj96XLQ77gEtH1-r8KsHw 提取码: 2333

二、项目进展

1、场景布置:

  • 游戏界面地图初始化
    超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
  • 第一关卡最终场景布置
    超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
  • 游戏音效
    超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
  • [x]素材图片
    超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
  • 动态效果

超级玛丽

2、基本角色及相关功能介绍:

马里奥:

*能够左右行走、跑步、跳跃;

*能够通过吃金币得分;

*马里奥吃到蘑菇会变大,拥有二次生命;

*顶碎砖块开辟道路;

*与敌人正面碰撞或者掉入深渊会死亡;

敌人:

*能够左右行走

*能够和马里奥大叔交互

*死亡消失

3、登录注册界面

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

4、开始结束界面

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划
超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

5、场景之间的跳转

可以把现阶段所以的场景合起来,实现每个不同场景之间的切换。

6、隐形关卡(增加难度)

马里奥从第一关卡某个管道进入这个挑战关卡,关卡隐形。

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

7、第二关卡基本地图设计完成

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

8、第三阶段基本地图设计完成

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

三、项目基本功能架构和功能流程图示

1、基本功能架构

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

2、相关类介绍及关联

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

designed by Modelio Open Source 4.0

下一步计划

  • 这一周把第所有关卡设计好
  • 第十五周综合测试所有关卡、维护项目
  • 第十五完善所有的设计文档,并细化制作细节
  • 第十六周报告展示

点击下方链接,访问我们的github

链接: https://github.com/CAdom/MySuperMario.

目前项目提交情况:

超级玛丽/超级马里奥超级玛丽/超级马里奥下一步计划

##关键代码展示

音频控制器

.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AudioManager : MonoBehaviour
{
    GameObject obj;
    AudioSource bgmPlayer;
    AudioSource sePlayer;

    //单例模式
    public static AudioManager Instance;//声明当前类静态实例
    private void Awake()
    {
        Instance = this;//this就代表当前这个类
    }

    void Start ()
    {
        obj = GameObject.Find("AudioPlayer");
        bgmPlayer = obj.GetComponent<AudioSource>();
        sePlayer = obj.GetComponent<AudioSource>();
    }

    public void PlayMusic(string name)
    {
        AudioClip clip = Resources.Load<AudioClip>("Audios/" + name);//加载音乐片段
        bgmPlayer.clip = clip;//切换音乐
        bgmPlayer.Play();//播放新的音乐
    }

    public void StopMusic()
    {
        bgmPlayer.Stop();
    }

    public void PlaySound(string name)
    {
        AudioClip clip = Resources.Load<AudioClip>("Audios/" + name);//加载特效片段
        sePlayer.PlayOneShot(clip);//播放加载的音频
    }
}
           

相机控制器

.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraControl : MonoBehaviour
{
    float minX;
    float maxX;
    public static Transform mario;

    void Start()
    {
        minX = -16;
        maxX = 16;
        mario = GameObject.Find(PlayerControl.wanjia).transform;
    }

    void Update()
    {
        //mario = GameObject.Find(PlayerControl.wanjia).transform;
        Vector3 pos = transform.position;//获取相机当前的位置
        pos.x = mario.position.x;//更改位置的X轴的位置
        if (pos.x > maxX)
        {
            pos.x = maxX;
        }
        if (pos.x < minX)
        {
            pos.x = minX;
        }

        transform.position = pos;//将更改后的值重新附给相机,实现相机位置更新
    }
}
           

马里奥大叔控制代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class PlayerControl : MonoBehaviour
{
   //public static int Hp.PlayerHp;//血量
    float f;//移动方向
    Animator ani;//玩家动画
    SpriteRenderer spr;//玩家精灵
    Rigidbody2D rig;//刚体
    bool isGround;//玩家是否在地面上
    RaycastHit2D dhit;//玩家发射的向下的射线所碰到的物体信息
    RaycastHit2D hit;//玩家向左或者右侧发射射线所碰到的物体信息
    public float aaa;
    public static string wanjia; //玩家状态
    public  GameObject PlayWj; //小马里奥
    public  GameObject HUQ; //加载火球
    public static GameObject HUQ2; //火球

    float tf;

    void Start ()
    {

         //Hp.PlayerHp = 1;//血量初始值为1
         wanjia = "Mario";
      
        ani = GetComponent<Animator>();//获取动画控制器组件
        spr = GetComponent<SpriteRenderer>();//获取精灵组件
        rig = GetComponent<Rigidbody2D>();//获取刚体组件
        isGround = true;
        PlayWj = (GameObject)Resources.Load("Mario");
        HUQ = (GameObject)Resources.Load("HuoQ");

        tf = 1;
    }
	
	void Update ()
    {
        Debug.Log(Hp.PlayerHp);

        f = Input.GetAxis("H");//按右为正,按左为负,不按为零

        if (Hp.PlayerHp > 0 && f != 0)
        {
            if (f < 0)//左走
            {
                spr.flipX = true;//反转
                hit = Physics2D.Raycast(transform.position,Vector2.left,0.1f,1 << 8);
            }
            else//右走
            {
                spr.flipX = false;//不反转
                hit = Physics2D.Raycast(transform.position, Vector2.right, 0.1f, 1 << 8);
            }

            if (!hit.collider)//左右没有墙
            {
                transform.Translate(transform.right * 1.5f * Time.deltaTime * f);
            }
            ani.SetBool("IsRun",true);
        }
        
        if(f == 0)
        {
            ani.SetBool("IsRun", false);
        }

        //发射射线并获取射线碰撞到的物体信息:发射起点为玩家自身坐标原点,方向为向下,射线长度为0.1f,只检测第8层
        dhit = Physics2D.Raycast(transform.position, Vector2.down, aaa, 1 << 8);
        if (dhit.collider)//射线碰到物体的碰撞器
        {
            isGround = true;
            ani.SetBool("IsJump", false);
        }
        else
        {
            isGround = false;
            ani.SetBool("IsJump", true);
        }

        //跳
        if (Input.GetKeyDown(KeyCode.Space) && isGround)//按下空格
        {
            rig.AddForce(Vector2.up * 200);
            AudioManager.Instance.PlaySound("跳");
        }

        if(Input.GetKeyDown(KeyCode.J))
        {
            HUQ2 = Instantiate(HUQ, transform.position, Quaternion.identity);
            HuoQ.FS2();

        }
        if (Input.GetKeyDown(KeyCode.K))
        {
            HUQ2 = Instantiate(HUQ, transform.position, Quaternion.identity);
            HuoQ.FS1();
        }

        if (gameObject.tag =="WuDi")
        {
            tf -= Time.deltaTime;

            if (tf <= 0)
            {
                gameObject.tag = "Player";
                tf = 1;
            }
        }
	}

    public void playdeth() //人物死亡
    {
        Hp.PlayerHp--;


        if(Hp.PlayerHp <= 0)
        {
           
            ani.SetTrigger("IsDead");
            AudioManager.Instance.StopMusic();
            AudioManager.Instance.PlaySound("死亡1");

            Invoke("Deth2",0.8f);
            SceneManager.LoadScene(2);
            Destroy(GetComponent<BoxCollider2D>()); //销毁组件
            rig.velocity = Vector2.zero; //速度归零 保护措施

            rig.AddForce(Vector2.up * 150); //死亡向上跳一下

            //Destroy(gameObject, 3);
            SceneManager.LoadScene(2);

        }

         if(Hp.PlayerHp == 1 && wanjia == "Mario")
        {
            GameObject da = GameObject.Find("Mario2");
            da.SetActive(false);
            GameObject obj = Instantiate(PlayWj, transform.position, Quaternion.identity);

            obj.tag = "WuDi";

            obj.name = "Mario";
            PlayerControl.wanjia = obj.name;

            CameraControl.mario = obj.transform;

            Destroy(da);

            AudioManager.Instance.PlaySound("吃到蘑菇或花");
        }
    }

    void Deth2() //死亡2音效
    {
        AudioManager.Instance.PlaySound("死亡2");
    }
}

           

敌人(蘑菇)的控制器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class EnemyControl : MonoBehaviour
{
    int Hp;//敌人血量
    int dir;//敌人移动的方向
    RaycastHit2D hit;
    Animator ani;

    void Start ()
    {
        Hp = 1;
        dir = -1;

        ani = gameObject.GetComponent<Animator>();
    }
	
	void Update ()
    {      if(Hp > 0)
        {
            transform.Translate(transform.right * 0.2f * Time.deltaTime * dir);
        }

        if (dir == 1)
        {
            hit = Physics2D.Raycast(transform.position +Vector3.up*0.1f, Vector2.right, 0.1f, ~(1 << 9));
        }
        else if (dir == -1)
        {
            hit = Physics2D.Raycast(transform.position+ Vector3.up * 0.1f, Vector2.left, 0.1f, ~(1 << 9));
        }

        if (hit.collider)
        {
            dir = -dir;
        }
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        dir = -dir;
        if(collision.collider.tag == "Player")
        {
            if(collision.contacts[0].normal == Vector2.down) //contacts数组代表接触点 第0个点。 normal:接触点垂直的法线。
            {

                collision.gameObject.GetComponent<Rigidbody2D>().AddForce(Vector2.up * 100);
                Emdeth();

            }
            else
            {
                collision.gameObject.GetComponent<PlayerControl>().playdeth(); //拿到碰撞人物 获取人物绑定的脚本,在调用方法。
               
            }
           
        }
   
    
    }
     
    private void OnTriggerEnter2D(Collider2D collision) //火球攻击
    {
        if (collision.tag == "TouSW")
        {
            Emdeth();
        }
    }

    void Emdeth() //敌人死亡
    {
        Hp--;
        if (Hp <= 0)
        {
            Destroy(gameObject,0.6f); //在0.6秒以后执行销毁。Destroy并不是立即销毁(在本帧结束之前),第二帧开始之前才会销毁。
            ani.SetTrigger("emenydeth");
            AudioManager.Instance.PlaySound("踩敌人");

            GetComponent<Collider2D>().enabled = false; //关闭组件(未激活)
            Destroy(GetComponent<Rigidbody2D>()); //销毁组件


        }
    }


}

           

登录设计

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using UnityEngine.UI;

public class Script_Login : MonoBehaviour {
    string txt_ID;
    string txt_Psw;
    Text txt_info;
    string filePath;

	// Use this for initialization
	void Start () {
        txt_info = GameObject.Find("Txt_Info").GetComponent<Text>();
        filePath = Application.dataPath + "/" + "users.txt";
    }
	
	// Update is called once per frame
	void Update () {
		
	}

    //********************************************
    public void Login()
    {
        txt_ID = GameObject.Find("AccountInput/Text").GetComponent<Text>().text;
        txt_Psw = GameObject.Find("PasswordInput/Text").GetComponent<Text>().text;
        if (txt_ID == "")
        {
            txt_info.text = "请输入账号";
            return;
        }
        if (txt_Psw == "")
        {
            txt_info.text = "请输入密码";
            return;
        }

        if (Check_Login(txt_ID, txt_Psw))
        {
            txt_info.text = "登录成功";
        }
        else
        {
            txt_info.text = "账号或密码错误";
        }
    }
    bool Check_Login(string id, string psw)
    {
        string[] Users = File.ReadAllLines(filePath);
        for (int i = 0; i < Users.Length; i++)
        {
            string user_id = Users[i].Split(' ')[0];
            string user_psw = Users[i].Split(' ')[1];
            if (id == user_id && psw == user_psw)
            {
                return true;
            }
        }
        return false;
    }
}

           

注册设计

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class Script_Regist : MonoBehaviour {
    string txt_ID;
    string txt_Psw;
    string txt_Psw_2;
    bool b_IfLoginSuccess;
    Text txt_info;
    string filePath;
    StreamWriter sw;

    // Use this for initialization
    void Start () {
        txt_info = GameObject.Find("Txt_Info").GetComponent<Text>();
        filePath = Application.dataPath + "/" + "users.txt";
    }
	
	// Update is called once per frame
	void Update () {
		
	}

    public void Regist()
    {
        txt_ID = GameObject.Find("InputF_ID/Text").GetComponent<Text>().text;
        txt_Psw = GameObject.Find("InputF_Psw/Text").GetComponent<Text>().text;
        txt_Psw_2 = GameObject.Find("InputF_Psw_2/Text").GetComponent<Text>().text;
        if (txt_ID == "")
        {
            txt_info.text = "请输入账号";
            return;
        }
        if (txt_Psw == "")
        {
            txt_info.text = "请输入密码";
            return;
        }
        if (txt_Psw_2 == "")
        {
            txt_info.text = "请输入密码";
            return;
        }

        if (txt_Psw != txt_Psw_2)
        {
            txt_info.text = "两次输入的密码不一致";
        }
        else
        {
            if (CheckID(txt_ID) == false)
            {
                txt_info.text = "已存在相同账号";
            }
            else
            {
                txt_info.text = "注册成功";
                //
                WriteUserInfo(txt_ID, txt_Psw);
            }
        }
    }

    void WriteUserInfo(string id, string psw)
    {
        if (!File.Exists(filePath))
        {
            sw = File.CreateText(filePath);
        }

        sw = File.AppendText(filePath);
        sw.WriteLine(id + " " + psw);
        sw.Close();
    }

    bool CheckID(string id)
    {
        string[] Users = File.ReadAllLines(filePath);
        for (int i = 0; i < Users.Length; i++)
        {
            string user_id = Users[i].Split(' ')[0];
            //string user_psw = Users[i - 1].Split( )[1];
            if (id == user_id)
            {
                return false;
            }
        }
        return true;
    }
}
           

团队成员任务分配

  • 团队成员

    队长:锁亚

    队员:庄子凡、张锦鸿、陈建枭、王大营

  • 任务分配

    锁亚:马里奥动画制作及第一关卡场景设置、登录注册界面、推送博客及展示

    庄子凡:第二关卡场景设置和角色控制

    张锦鸿:开始界面和结束界面,及隐形关卡场景设置、关卡之间的转换

    陈建枭:第一阶段场景布置及敌人相关角色控制

    王大营:第三阶段场景制作和角色控制

未完待续。。。

继续阅读