24U3D开发之2D拾荒者

2D游戏拾荒者开发##

效果展示

一、预支物的制作##

1.获取资源: https://pan.baidu.com/s/1qYAozV2
2.图片的截取.

截取图片
截取效果

3.Player和Enemy的动画制作.

1.把主角的6张图片同时拖入到Hierarchy面板中,制动创建Animator,有Controller,和对应的动画.
2.在把攻击的图片拖上去,自动创建攻击动画.同理把收到伤害的图片拖上去,得到受到伤害的动画.

制作过程
完成效果
Enemy敌人的制作
和Player的制作一样,但是我们在制作Enemy2的时候,可以不用从新创建一个Controller了.可以创建一个
Animator Override Controller,通过Enemy1创建Enemy2的动画,改变图片从新创建一个新的预支物就可以.


OverriderController

3.Floor,OutWall的Prefab的制作.

Floor,OutWall的制作

二、创建地图##

游戏范围展示
这里涉及到层的概念:我们要设计好层次关系,不然系统识别不了那个在前面.
我们吧外墙(OutWall),地板(Floor)设置为最底层(Background).
食物(friut,soda)设置为Item,敌人和Player设置为Role层.
层次的设定
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>();//用来保存在敌人,障碍物生成区域的位置信息
    private GameManger gameManager;




    int minCountWall = 2;//最少内墙个数
    int maxCountWall = 8;//最多内墙个数,随机生成




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

    }
    
    
    void Update () {
        
    }

    public void InitMap()
    {
        mapHolder = new GameObject("Map").transform;//穿件一个空物体用来管理生成的地图
        //创建外围墙和地板
        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();//承储敌人,障碍物区域的信息到list里面。
        for (int i = 2; i < line-2; i++)
        {
            for (int j = 2; j < row-2; j++)
            {
                positionList.Add(new Vector2(i,j));
            }
        }

        //创建障碍物
        int wallCount = Random.Range(minCountWall, maxCountWall + 1);
        InstantiateItem(wallCount,walls);

        //创建食物2-level*2
        int foodCount = Random.Range(2, GameManger.level*2+1);//创建的敌人个数与关卡有关系
        InstantiateItem(foodCount,foods);

        //创建敌人level/2
        int enemyCount = GameManger.level/ 2;
        InstantiateItem(enemyCount,enemys);

        //创建出口
        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);//在把这个地点信息移除,避免在同一个地点出现多个物体。
        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过渡参数详解
Player动画Transition过渡参数详解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;//设置为不可用,不然会检测到自己
            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;
                }
            }
            //设置目标位置之前,先做检测;
            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;//当前关卡
    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);

        //初始化参数
        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;
            //加载下一个关卡
            SceneManager.LoadScene(0);
            //Application.LoadLevel(Application.loadedLevel);//从新加载本关卡
        }
    }

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

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

七、Player对敌人,食物,和墙里面障碍物的处理##

对内墙的攻击处理:
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

九、声音文件的添加##

声音文件很重要,在适当的时候,合适的位置添加声音文件.
脚本管理:
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()//游戏结束时,结束背景音乐的播放
    {
        bgSource.Stop();
    }



}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容