有了前面三章的学习,我们试着做一个小游戏来强化学习的技术,比如做一个 推箱子Sokoban 游戏;
(很多类容都要用到前一章知识:
https://blog.csdn.net/mww584161068/article/details/79986005)
(由于没有美工给图,随便将就用点图,以实现功能为主)
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.
3.
4.
5.
玩家,箱子(这里以怪物代替),洞。
首先是玩家:
滑动代替玩家走动,碰到墙或者障碍物不能走动,碰到箱子能推过去,如果箱子那边还有阻碍则推不动,自然想到用射线检测处理比较好。
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++;
}
}
很简单的脚本,主要是为了检测是否成功。
没有什么好贴图,就这样完成了。
(ui制作,界面跳转逻辑,关卡加载给略过了)
然后根据地图编辑器,把每个关不同摆放(其实这个算策划工作),在写每个关点进去不同场景(如上代码)的加载 这个游戏就算完成了,只剩下后期优化了。