24U3D開發(fā)之2D拾荒者

2D游戲拾荒者開發(fā)##

效果展示

一、預支物的制作##

1.獲取資源: https://pan.baidu.com/s/1qYAozV2
2.圖片的截取.

截取圖片
截取效果

3.Player和Enemy的動畫制作.

1.把主角的6張圖片同時拖入到Hierarchy面板中,制動創(chuàng)建Animator,有Controller,和對應的動畫.
2.在把攻擊的圖片拖上去,自動創(chuàng)建攻擊動畫.同理把收到傷害的圖片拖上去,得到受到傷害的動畫.

制作過程
完成效果
Enemy敵人的制作
和Player的制作一樣,但是我們在制作Enemy2的時候,可以不用從新創(chuàng)建一個Controller了.可以創(chuàng)建一個
Animator Override Controller,通過Enemy1創(chuàng)建Enemy2的動畫,改變圖片從新創(chuàng)建一個新的預支物就可以.


OverriderController

3.Floor,OutWall的Prefab的制作.

Floor,OutWall的制作

二、創(chuàng)建地圖##

游戲范圍展示
這里涉及到層的概念:我們要設(shè)計好層次關(guān)系,不然系統(tǒng)識別不了那個在前面.
我們吧外墻(OutWall),地板(Floor)設(shè)置為最底層(Background).
食物(friut,soda)設(shè)置為Item,敵人和Player設(shè)置為Role層.
層次的設(shè)定
Map管理腳本在Inspector中展示
腳本代碼管理:
public class MapManager : MonoBehaviour {


    public GameObject[] OutWalls;
    public GameObject[] Floors;
    public GameObject[] walls;
    public GameObject[] foods;
    public GameObject[] enemys;
    public GameObject exitPrefab;

    public  int line = 10;
    public  int row = 10;
    Transform mapHolder;

    private List<Vector2> positionList = new List<Vector2>();//用來保存在敵人,障礙物生成區(qū)域的位置信息
    private GameManger gameManager;




    int minCountWall = 2;//最少內(nèi)墻個數(shù)
    int maxCountWall = 8;//最多內(nèi)墻個數(shù),隨機生成




    void Awake () {
        gameManager = this.GetComponent<GameManger>();
        //InitMap();

    }
    
    
    void Update () {
        
    }

    public void InitMap()
    {
        mapHolder = new GameObject("Map").transform;//穿件一個空物體用來管理生成的地圖
        //創(chuàng)建外圍墻和地板
        for (int i = 0; i < line; i++)
        {
            for (int j = 0; j < row; j++)
            {
                if (i == 0 || i == line - 1 || j == 0 || j == row - 1)
                {
                    int index = Random.Range(0, OutWalls.Length);
                    GameObject go=Instantiate(OutWalls[index], new Vector3(i, j, 0), Quaternion.identity)as GameObject;
                    go.transform.SetParent(mapHolder);
                }
                else
                {
                    int index = Random.Range(0, Floors.Length);
                    GameObject go=Instantiate(Floors[index],new Vector3(i,j,0),Quaternion.identity)as GameObject;
                    go.transform.SetParent(mapHolder);

                }
            }
        }

        positionList.Clear();//承儲敵人,障礙物區(qū)域的信息到list里面。
        for (int i = 2; i < line-2; i++)
        {
            for (int j = 2; j < row-2; j++)
            {
                positionList.Add(new Vector2(i,j));
            }
        }

        //創(chuàng)建障礙物
        int wallCount = Random.Range(minCountWall, maxCountWall + 1);
        InstantiateItem(wallCount,walls);

        //創(chuàng)建食物2-level*2
        int foodCount = Random.Range(2, GameManger.level*2+1);//創(chuàng)建的敵人個數(shù)與關(guān)卡有關(guān)系
        InstantiateItem(foodCount,foods);

        //創(chuàng)建敵人level/2
        int enemyCount = GameManger.level/ 2;
        InstantiateItem(enemyCount,enemys);

        //創(chuàng)建出口
        GameObject go1= Instantiate(exitPrefab, new Vector2(line - 2, row - 2), Quaternion.identity) as GameObject;
        go1.transform.SetParent(mapHolder);
    }

    private Vector2 RandomPosition()
    {
        int positionIndex = Random.Range(0, positionList.Count);
        Vector2 pos = positionList[positionIndex];//隨機拿到一個位置List里面的地點信息
        positionList.RemoveAt(positionIndex);//在把這個地點信息移除,避免在同一個地點出現(xiàn)多個物體。
        return pos;
    }

    private GameObject RandomPrefab(GameObject[]prefabs)
    {
        int index = Random.Range(0, prefabs.Length);
        return prefabs[index];
    }

    private void InstantiateItem(int count,GameObject[] prefabs)
    {
        for (int i = 0; i < count; i++)
        {
            Vector2 pos = RandomPosition();
            GameObject enemyPrefab = RandomPrefab(prefabs);
            GameObject go = Instantiate(enemyPrefab, pos, Quaternion.identity);
            go.transform.SetParent(mapHolder);
        }
    }



}

三、Player和Enemy的動畫完善##

Player動畫Transition過渡參數(shù)詳解
Player動畫Transition過渡參數(shù)詳解2
Enemy動畫完善

四、主角(Player)的移動邏輯##

主角身上的腳本控制
腳本展示:
public class Player : MonoBehaviour {


    public float smoothing = 1;
    public float restTime = 1;

    //聲音文件
    public AudioClip chop1Audio;
    public AudioClip chop2Audio;
    public AudioClip step1Audio;
    public AudioClip step2Audio;

    public AudioClip soda1Audio;
    public AudioClip soda2Audio;
    public AudioClip fruit1Audio;
    public AudioClip fruit2Audio;

    private float restTimer = 0;
    public Animator animator;
   
    [HideInInspector]public Vector2 targetPos = new Vector2(1, 1);//主角開始的位置
    private Rigidbody2D rigibody;
    private BoxCollider2D collider;


    
    void Start () {
        rigibody = GetComponent<Rigidbody2D>();
        collider = GetComponent<BoxCollider2D>();
        animator=GetComponent<Animator>();
    }
    
    
    void Update () {
        rigibody.MovePosition(Vector2.Lerp(transform.position, targetPos, smoothing * Time.deltaTime));

       // if (GameManger.Instance.food <= 0) return;

        restTimer += Time.deltaTime;
        if (restTimer < restTime) return;
        float h = Input.GetAxisRaw("Horizontal");//只會返回-1,0,1
        float v = Input.GetAxisRaw("Vertical");
        //print(h);
        //print(v);
        if (h>0)
        {
            v = 0;
        }
              
        if (h != 0 || v != 0)
        {
            GameManger.Instance.ReduceFood(1);
            //檢測
            collider.enabled = false;//設(shè)置為不可用,不然會檢測到自己
            RaycastHit2D hit=Physics2D.Linecast(targetPos,targetPos+new Vector2(h,v));
            collider.enabled = true;
            if (hit.transform == null)
            {
                targetPos += new Vector2(h, v);
                AudioManager._instance.RandomPlay(step1Audio,step2Audio);
               
            }
            else
            {
                switch (hit.collider.tag)
                {
                    case "OutWall":
                        break;
                    case "Wall":
                        animator.SetTrigger("Attack");
                        AudioManager._instance.RandomPlay(chop1Audio, chop2Audio);
                        hit.collider.SendMessage("TakeDamage");                     
                        break;
                    case "Food":
                        GameManger.Instance.AddFood(10);
                        targetPos += new Vector2(h, v);
                        AudioManager._instance.RandomPlay(step1Audio, step2Audio);
                        Destroy(hit.transform.gameObject);
                        AudioManager._instance.RandomPlay(fruit1Audio,fruit2Audio);
                        break;
                    case "Soda":
                        GameManger.Instance.AddFood(20);
                        targetPos += new Vector2(h, v);
                        AudioManager._instance.RandomPlay(step1Audio, step2Audio);
                        Destroy(hit.transform.gameObject);
                        AudioManager._instance.RandomPlay(soda1Audio, soda2Audio);
                        break;
                    case "Enemy":
                        break;
                }

            }
            GameManger.Instance.OnPlayerMove();//沒幀檢測有沒有到達終點
            restTimer = 0;

        }
        if (GameManger.Instance.food==0||GameManger.Instance.isEnd==true)
        {
            this.enabled = false;
        }
    }

    public void TakeDamage(int lossFood)
    {
        GameManger.Instance.ReduceFood(lossFood);
        animator.SetTrigger("Damager");
    }
}

五、控制敵人的移動##

敵人Enemy展示
代碼展示:
public class Enemy : MonoBehaviour {

    private Vector2 targetPosition;
    private Transform player;
    private Rigidbody2D rigidbody;

    public float smoothing = 3;
    private BoxCollider2D collider;
    private Animator animator;
    public int lossFood = 10;

    public AudioClip attackAudio;

    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player").transform;
        targetPosition = transform.position;
        rigidbody = GetComponent<Rigidbody2D>();
        collider = GetComponent<BoxCollider2D>();
        animator = GetComponent<Animator>();
        GameManger.Instance.enemyList.Add(this);//保存敵人到集合里面

    }

    void Update()
    {
        rigidbody.MovePosition(Vector2.Lerp(transform.position,targetPosition,smoothing*Time.deltaTime));
    }


    
    public void Move()
    {
        Vector2 offset = player.position - transform.position;
        if (offset.magnitude < 1.1f)
        {
            //攻擊
            animator.SetTrigger("Attack");
            AudioManager._instance.RandomPlay(attackAudio);
            player.SendMessage("TakeDamage",lossFood);

        }
        else
        {
            float x = 0;
            float y = 0;
            if (Mathf.Abs(offset.y) > Mathf.Abs(offset.x))//按照y軸移動
            {
                if (offset.y < 0)//向反向
                {
                    y = -1;
                }
                else//正向
                {
                    y = 1;
                }
            }
            else//按照x軸移動
            {
                if (offset.x > 0)
                {
                    x = 1;
                }
                else
                {
                    x = -1;
                }
            }
            //設(shè)置目標位置之前,先做檢測;
            collider.enabled = false;
            RaycastHit2D hit = Physics2D.Linecast(targetPosition, targetPosition+ new Vector2(x, y));
            collider.enabled = true;
            if (hit.transform == null)
            {
                targetPosition += new Vector2(x, y);
            }
            else
            {
                if (hit.collider.tag=="Food"||hit.collider.tag=="Soda")
                {
                    targetPosition += new Vector2(x, y);
                }
            }
            
        }
    }




}


六、游戲管理腳本(對整個游戲進行管理)##

public class GameManger : MonoBehaviour {

    public static int level = 1;//當前關(guān)卡
    public int food = 100;

    public AudioClip dieClip;


    private static GameManger _instance;

    public static GameManger Instance
    {
        get
        {
            return _instance;
        }
    }

    [HideInInspector]public bool isEnd = false;
    public List<Enemy> enemyList = new List<Enemy>();
    private bool sleepStep = true;//控制敵人移動的限定;
    private Text foodText;
    private Text failText;
    private Image dayImage;
    private Text dayText;
    private Player player;
    private MapManager mapManager;

    void Awake()
    {
       
        _instance = this;
        //DontDestroyOnLoad(gameObject);
        InitGame();

    }


    void InitGame()
    {
        //初始化地圖
        mapManager = GetComponent<MapManager>();
        mapManager.InitMap();
        food = PlayerPrefs.GetInt("Food", 10);
        //初始化UI
        foodText = GameObject.Find("FoodText").GetComponent<Text>();
        UpdateFoodText(0);
        failText = GameObject.Find("FailText").GetComponent<Text>();
        failText.enabled = false;
        player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
        mapManager = GetComponent<MapManager>();
        dayImage = GameObject.Find("DayImage").GetComponent<Image>();
        dayText = GameObject.Find("DayText").GetComponent<Text>();
        dayText.text = "Day" + " " + level;
        Invoke("HideBlack", 1);

        //初始化參數(shù)
        isEnd = false;
        enemyList.Clear();
    }

    void UpdateFoodText(int foodChange)
    {
        if (foodChange == 0)
        {
            foodText.text = "Food:" + food;
        }
        else
        {
            string str = "";
            if (foodChange<0)
            {
                str = foodChange.ToString();
            }
            else
            {
                str = "+" + foodChange;
            }
            foodText.text = str+" " + "Food:" + food;
        }
        
    }



    public void ReduceFood(int count)
    {
        food -= count;
        UpdateFoodText(-count);
        if(food<=0)
        {
            failText.enabled = true;
            AudioManager._instance.StopBgMusic();
            AudioManager._instance.RandomPlay(dieClip);
        }
    }

    public void AddFood(int count)
    {
        food += count;
        UpdateFoodText(count);
    }


    public void OnPlayerMove()//通知敵人行走,邏輯,主角走2步,敵人走一步
    {
        if (sleepStep == true)
        {
            sleepStep = false;
        }
        else
        {
            foreach (var enemy in enemyList)
            {
                enemy.Move();
            }
            sleepStep = true;
        }
        //檢測有沒有到達終點
        if (player.targetPos.x==mapManager.line-2&&player.targetPos.y==mapManager.row-2)
        {
            PlayerPrefs.SetInt("Food", food);
            isEnd = true;
            //加載下一個關(guān)卡
            SceneManager.LoadScene(0);
            //Application.LoadLevel(Application.loadedLevel);//從新加載本關(guān)卡
        }
    }

    void OnLevelWasLoaded()
    {
        //PlayerPrefs.GetFloat("level", 1);
        level++;
        //PlayerPrefs.SetFloat("level", level);
       // InitGame();
    }

    void HideBlack()
    {
        dayImage.gameObject.SetActive(false);    
    }
}

七、Player對敵人,食物,和墻里面障礙物的處理##

對內(nèi)墻的攻擊處理:
public class Wall : MonoBehaviour {

    public int hp = 2;
    public Sprite damageSprite;//受到攻擊的圖片


    public void TakeDamage()
    {
        hp -= 1;
        GetComponent<SpriteRenderer>().sprite = damageSprite;
        if(hp==0)
        {
            Destroy(this.gameObject);
        }
    }
}

八、食物的UI處理##

UI處理
UI處理2

九、聲音文件的添加##

聲音文件很重要,在適當?shù)臅r候,合適的位置添加聲音文件.
腳本管理:
public class AudioManager : MonoBehaviour {

    public float minPitch = 0.9f;
    public float maxPitch = 1.1f;

    public static AudioManager _instance;

    public AudioSource efxSouce;
    public AudioSource bgSource;

    void Awake()
    {
        _instance = this;
    }
   


    public void RandomPlay(params AudioClip[]clips)//播放聲音文件
    {
        float pitch = Random.Range(minPitch,maxPitch);
        int index = Random.Range(0,clips.Length);
        AudioClip clip = clips[index];
        efxSouce.clip = clip;
        efxSouce.pitch = pitch;//播放速度
        efxSouce.Play();


    }

    public void StopBgMusic()//游戲結(jié)束時,結(jié)束背景音樂的播放
    {
        bgSource.Stop();
    }



}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容