第四章,实战 推箱子游戏

有了前面三章的学习,我们试着做一个小游戏来强化学习的技术,比如做一个 推箱子Sokoban 游戏;

一.由于每关地图不一样,首先得想到的是做一个地图关卡编辑器

(很多类容都要用到前一章知识:

https://blog.csdn.net/mww584161068/article/details/79986005)

(由于没有美工给图,随便将就用点图,以实现功能为主)

 1,把图都做成预制体。

    第四章,实战 推箱子游戏_第1张图片

2.然后编辑需要加载这些预制体的脚本

(借助前一章的一些脚本,就不贴了

https://blog.csdn.net/mww584161068/article/details/79986005)

public class MapEditor :ViewEditor {

    static int selectMum;

    static int widthMum=1;
    static int heightMum=1;

    GameObject wallH;
    GameObject wallV;
    GameObject leftUp;
    GameObject rightUp;
    GameObject rightDown;
    GameObject leftDown;

    GameObject light;
    GameObject dark;


    protected override void OnGUI()
    {
        if (MwwWindow.instance != null)
        {
            selectMum = MwwWindow.instance.DrawViewSelect(selectMum);

            EditorGUILayout.Space();
      
            widthMum = EditorGUILayout.IntField("宽度:", widthMum);
            heightMum = EditorGUILayout.IntField("高度:", heightMum);
            EditorGUILayout.Space();
            EditorGUILayout.Space();

            if (GUILayout.Button("生成边框"))
            {
                Create();
            }
        }
    }

    void Init()
    {
        wallH= (GameObject)Resources.Load("Prefab/H", typeof(GameObject));
        wallV = (GameObject)Resources.Load("Prefab/V", typeof(GameObject));
        leftUp= (GameObject)Resources.Load("Prefab/left_up", typeof(GameObject));
        rightUp= (GameObject)Resources.Load("Prefab/right_up", typeof(GameObject));
        rightDown = (GameObject)Resources.Load("Prefab/right_down", typeof(GameObject));
        leftDown= (GameObject)Resources.Load("Prefab/left_down", typeof(GameObject));

        light = (GameObject)Resources.Load("Prefab/light", typeof(GameObject));
        dark = (GameObject)Resources.Load("Prefab/dark", typeof(GameObject));
    }

    const float leftUp_wallH_V = 0.03f;

    const float leftUp_wallH=0.59f;
    const float wallH_wallH = 0.678f;
    const float wallH_rightUp = 0.475f;

    const float leftUp_wallV = -0.04f;
    const float rightUp_wallV = 0.118f;

    const float leftUp_wallV_V = -0.7f;
    const float wallV_wallV_V = -0.6f;


    const float wallV_leftDown = -0.65f;

    const float rightDown_rightUP = 0.055f;
    const float leftDown_wallH=-0.14f;

    GameObject parent;

    const float lightMum = 0.55f;

    void Create()
    {
        Init();

        parent = GameObject.Find("level");
        if (parent != null)
        {

            GameObject mapBorder = new GameObject("Border");
            mapBorder.transform.parent = parent.transform;

            LoadObject.LoadPrefab(leftUp, mapBorder,Vector3.zero);

            for (int i = 0; i < widthMum; i++)
            {
                LoadObject.LoadPrefab(wallH, mapBorder, new Vector3(leftUp_wallH + i * wallH_wallH, leftUp_wallH_V, 0));
            }

            LoadObject.LoadPrefab(rightUp, mapBorder, new Vector3(leftUp_wallH + (widthMum-1) * wallH_wallH + wallH_rightUp, 0,0));

            for (int i = 0; i < heightMum; i++)
            {
                LoadObject.LoadPrefab(wallV, mapBorder, new Vector3(leftUp_wallV, leftUp_wallV_V + i * wallV_wallV_V, 0))
                    .GetComponent().sortingOrder = i + 1;

                LoadObject.LoadPrefab(wallV, mapBorder, new Vector3(leftUp_wallH + (widthMum - 1) * wallH_wallH + wallH_rightUp + rightUp_wallV, leftUp_wallV_V + i * wallV_wallV_V, 0))
                     .GetComponent().sortingOrder = i + 1;
            }

            LoadObject.LoadPrefab(leftDown, mapBorder, new Vector3(0, leftUp_wallV_V +(heightMum-1) * wallV_wallV_V+ wallV_leftDown, 0))
                .GetComponent().sortingOrder = widthMum + 1;       
            
            LoadObject.LoadPrefab(rightDown, mapBorder, new Vector3(leftUp_wallH + (widthMum - 1) * wallH_wallH + wallH_rightUp+rightDown_rightUP,
                leftUp_wallV_V + (heightMum - 1) * wallV_wallV_V + wallV_leftDown- leftUp_wallH_V, 0))
                .GetComponent().sortingOrder = widthMum + 1;

            for (int i = 0; i < widthMum; i++)
            {
                LoadObject.LoadPrefab(wallH, mapBorder, new Vector3(leftUp_wallH + i * wallH_wallH, leftUp_wallV_V + (heightMum - 1) * wallV_wallV_V + wallV_leftDown+leftDown_wallH, 0))
                                    .GetComponent().sortingOrder = widthMum + 1;
            }


            for (int i = 0; i < heightMum+1; i++)
            {
                for (int j = 0; j < widthMum+3; j++)
                {
                    if ((i + j) % 2 == 0)
                        LoadObject.LoadPrefab(light, mapBorder, new Vector3(j * lightMum - 0.02f, -(i + 1) * lightMum, 0));
                    else
                        LoadObject.LoadPrefab(dark, mapBorder, new Vector3(j * lightMum - 0.02f, -(i + 1) * lightMum, 0));
                }
            }
        }
    }
}

 

这一个脚本主要是先把预制体找到,然后根据要求点击按钮绘制一个矩形边框;(可自行替换别的需求)

 

效果操作如下:

1.

第四章,实战 推箱子游戏_第2张图片

2.

第四章,实战 推箱子游戏_第3张图片

3.

第四章,实战 推箱子游戏_第4张图片

4.

第四章,实战 推箱子游戏_第5张图片

5.

第四章,实战 推箱子游戏_第6张图片

二.然后就是实现功能了。

玩家,箱子(这里以怪物代替),洞。

首先是玩家:

滑动代替玩家走动,碰到墙或者障碍物不能走动,碰到箱子能推过去,如果箱子那边还有阻碍则推不动,自然想到用射线检测处理比较好。

using UnityEngine;

public class Player : ColliderBase {

    public LayerMask hitMask;//射线需要检验的层
    RaycastHit2D hit;//射线检验

    const float offsetCenter = 0.4f;
    const float unitDistance = 0.55f;

    public Animator thisAnim;//当前动画

    public float moveSpeed;//移动速度

    bool isPushMonster;
    Transform monsterTransform;
    Vector3 monsterTarget;//箱子的目标点


    Vector2 startPoint;//记录鼠标开始点
    int offsetOrder;//图层的改变

    protected override void OnEnable()
    {
        thisCollider.enabled = true;
        isMove = false;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            startPoint = Input.mousePosition;

        }
        if (Input.GetMouseButtonUp(0))
        {
            if (!isMove)
            {
                if (Vector2.Angle(Vector2.up, (Vector2)Input.mousePosition - startPoint) < 45f)
                {
                    Slide(Vector2.up);
                    offsetOrder = -1;

                }

                if (Vector2.Angle(Vector2.down, (Vector2)Input.mousePosition - startPoint) < 45f)
                {
                    Slide(Vector2.down);
                    offsetOrder = 1;
                }

                if (Vector2.Angle(Vector2.right, (Vector2)Input.mousePosition - startPoint) < 45f)
                {
                    Slide(Vector2.right);
                    offsetOrder = 0;
                }

                if (Vector2.Angle(Vector2.left, (Vector2)Input.mousePosition - startPoint) < 45f)
                {
                    Slide(Vector2.left);
                    offsetOrder = 0;
                }
            }
        }
        if (isMove)
        {
            thisTransform.position = Vector3.MoveTowards(thisTransform.position, targetPoint, moveTime);
            if (isPushMonster)
                monsterTransform.position= Vector3.MoveTowards(monsterTransform.position, monsterTarget, moveTime);
            moveTime += moveSpeed * Time.deltaTime;
            if (Vector3.Distance(thisTransform.position, targetPoint) < 0.1f)
            {
                isMove = false;
                moveTime = 0;
                thisTransform.GetComponent().sortingOrder += offsetOrder;
                if (isPushMonster)
                    monsterTransform.GetComponent().sortingOrder += offsetOrder;
                isPushMonster = false;
            }
        }
    }



    void Slide(Vector3 slideDirection)
    {    
        if (hit=Physics2D.Raycast(thisTransform.position + Vector3.down * offsetCenter + slideDirection * 0.5f, Vector3.up, 0.1f,hitMask))
        {
            if (hit.collider.GetComponent() != null)//monster为箱子脚本(这里用怪物代替)
            {
                if (Physics2D.Raycast(thisTransform.position + Vector3.down * offsetCenter + 2 * slideDirection * 0.5f, Vector3.up, 0.1f,hitMask).collider==null)
                {
                    isPushMonster = true;
                    isMove = true;
                    monsterTransform = hit.transform;
                    monsterTarget= monsterTransform.position + slideDirection * unitDistance;
                    targetPoint = thisTransform.position + slideDirection * unitDistance;
                }
            }
        }
        else
        {
            isPushMonster = false;
            isMove = true;
            targetPoint = thisTransform.position + slideDirection * unitDistance;
        }
    }
}

 

这里利用玩家脚本把各个游戏逻辑实现了。再补充

 

箱子脚本(脚本可以什么都不写,只要玩家脚本能检测的到即可)

然后根据之前做的地图,在各个物体上加上合适的碰撞器,

注意,地洞的碰撞器只跟箱子(怪物)发生碰撞。将地洞的碰撞器改为触发器。

地洞脚本:

public class Hole : MonoBehaviour {


    public static int HoleMum;

    private void OnEnable()
    {
        HoleMum++;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        HoleMum--;

        if (HoleMum <= 0)
        {
            UIControl.instance.EndBGChange(true);//游戏成功,跳到结束界面或者写游戏成功的逻辑
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        HoleMum++;
    }

}

 

 

很简单的脚本,主要是为了检测是否成功。

 

 

第四章,实战 推箱子游戏_第7张图片

没有什么好贴图,就这样完成了。

三、游戏运行

(ui制作,界面跳转逻辑,关卡加载给略过了)

第四章,实战 推箱子游戏_第8张图片

第四章,实战 推箱子游戏_第9张图片

第四章,实战 推箱子游戏_第10张图片

 

第四章,实战 推箱子游戏_第11张图片

然后根据地图编辑器,把每个关不同摆放(其实这个算策划工作),在写每个关点进去不同场景(如上代码)的加载 这个游戏就算完成了,只剩下后期优化了。

 

 

 

你可能感兴趣的:(unity)