首页 > 代码库 > [Unity XLua]热更新XLua入门(二)-俄罗斯方块实例篇
[Unity XLua]热更新XLua入门(二)-俄罗斯方块实例篇
前言
在xLua没出来之前,开源的lua框架基本都是以界面用Lua开发为主,核心战斗用C#开发,但xLua出来之后主推C#开发,Lua用作HotFix,这里我展示的第一个例子就是基于界面的经典2D小游戏——俄罗斯方块,界面逻辑是用C#写,启动加载逻辑是用lua,后面我会继续第二个同样的Demo,但是以纯Lua为主,这个案例明天更新。
效果图
由于我不会美术,所以这里使用的开源的游戏资源,感谢此作者。
游戏启动
C#启动Lua逻辑
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using XLua;
using System;
[LuaCallCSharp]
public class LuaRunningBehaviour : MonoBehaviour
{
public TextAsset luaScript;
internal static LuaEnv luaEnv = new LuaEnv(); //all lua behaviour shared one luaenv only!
internal static float lastGCTime = 0;
internal const float GCInterval = 1;//1 second
private Action luaStart;
private Action luaUpdate;
private Action luaOnDestroy;
private LuaTable scriptEnv;
void Awake()
{
scriptEnv = luaEnv.NewTable();
LuaTable meta = luaEnv.NewTable();
meta.Set("__index", luaEnv.Global);
scriptEnv.SetMetaTable(meta);
meta.Dispose();
scriptEnv.Set("self", this);
luaEnv.DoString(luaScript.text, "LuaBehaviour", scriptEnv);
Action luaAwake = scriptEnv.Get<Action>("awake");
scriptEnv.Get("start", out luaStart);
scriptEnv.Get("update", out luaUpdate);
scriptEnv.Get("ondestroy", out luaOnDestroy);
if (luaAwake != null)
{
luaAwake();
}
}
// Use this for initialization
void Start()
{
if (luaStart != null)
{
luaStart();
}
}
// Update is called once per frame
void Update()
{
if (luaUpdate != null)
{
luaUpdate();
}
if (Time.time - LuaBehaviour.lastGCTime > GCInterval)
{
luaEnv.Tick();
LuaBehaviour.lastGCTime = Time.time;
}
}
void OnDestroy()
{
if (luaOnDestroy != null)
{
luaOnDestroy();
}
luaOnDestroy = null;
luaUpdate = null;
luaStart = null;
scriptEnv.Dispose();
}
}
Lua加载游戏界面预设逻辑
local util = require ‘xlua.util‘
local function initGamePanel()
local obj = CS.UnityEngine.Resources.Load("TetrisPanelOne")
local gamePanel = CS.UnityEngine.Object.Instantiate(obj.gameObject).transform
gamePanel.name = "GamePanel"
gamePanel.gameObject.name = "GamePanel"
gamePanel:SetParent(self.transform)
gamePanel.localPosition = CS.UnityEngine.Vector3.zero
gamePanel.localScale = CS.UnityEngine.Vector3.one
end
function start()
print("lua start, 游戏开始...")
initGamePanel()
end
function ondestroy()
print("lua destroy")
end
游戏主要界面对应的C#逻辑
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
public class MainGameCSScript : MonoBehaviour
{
const int cRow = 16;
const int cCol = 10;
List<int>[] aTable = new List<int>[cRow];
int lineCount = 0;
public Texture[] BoxTexture = new Texture[4];
private string[] boxColors = new string[] { "eluosi_lan", "eluosi_zi", "eluosi_lv", "eluosi_hong" };
public int LineCount
{
get { return lineCount; }
set
{
lineCount = value;
this.onLineCountChange();
}
}
int score = 0;
public int Score
{
get { return score; }
set
{
score = value;
this.onScoreChange();
}
}
int nextCellID = 0;
int curCellID = 0;
float curSpeed = 1;
float fCurSpeed = 1;
float fDeltaTime = 0;
bool bPause = false;
bool bStart = false;
bool bGameOver = false;
public bool BGameOver
{
get { return bGameOver; }
set
{
bGameOver = value;
this.onGameOverChange(bGameOver);
}
}
System.Random rdm = new System.Random();
const int boxTop = 16;
const int boxLeft = 290;
/// <summary>
/// 彩色砖块的宽度
/// </summary>
const int boxSize = 38;
int[][] nextCell = null;
int[][] curCell = null;
int curCellCol = 0;
int curCellRow = 0;
public GameObject BtnUp, BtnDown, BtnLeft, BtnRight, BtnIsPause;
public UILabel TimeLabel, ScoreLabel, LineCountLabel;
private Action onScoreChange = null;
private Action onLineCountChange = null;
private Action<bool> onGameOverChange = null;
public GameObject EndPanel, BtnRestart;
public UISprite[] BoxSprites = null;
void Start()
{
BoxSprites = new UISprite[4];
for (int i = 0; i < 4; i++)
{
BoxTexture[i] = Resources.Load<Texture>(boxColors[i]);
}
this.onScoreChange = this.ScoreChange;
this.onLineCountChange = this.LineCountChange;
this.onGameOverChange = this.GameOverChange;
TimeLabel = transform.Find("LeftNode/CutDownBG/CountLabel").GetComponent<UILabel>();
LineCountLabel = transform.Find("ScoreNode/LineCount").GetComponent<UILabel>();
ScoreLabel = transform.Find("ScoreNode/Score").GetComponent<UILabel>();
BtnLeft = transform.Find("InputNode/Left").gameObject;
BtnRight = transform.Find("InputNode/Right").gameObject;
BtnUp = transform.Find("InputNode/Up").gameObject;
BtnDown = transform.Find("InputNode/Down").gameObject;
BtnIsPause = transform.Find("LeftNode/RestartBtn").gameObject;
EndPanel = transform.Find("EndPanel").gameObject;
BtnRestart = transform.Find("EndPanel/ReplayBtn").gameObject;
for (int i = 1; i <= 4; i++)
{
var obj = transform.Find(string.Format("BlcokTop/Blocks/block{0}", i)).gameObject;
var sprite = obj.GetComponent<UISprite>();
BoxSprites[i - 1] = sprite;
}
UIEventListener.Get(BtnRestart).onClick = go =>
{
EndPanel.gameObject.SetActive(false);
Debug.Log("重新开始");
DoStart();
};
UIEventListener.Get(BtnIsPause).onClick = go =>
{
Debug.Log("开始/暂停");
bPause = !bPause;
};
UIEventListener.Get(BtnUp).onClick = go =>
{
Debug.Log("上翻");
DoTransform();
};
UIEventListener.Get(BtnDown).onPress = (go, state) =>
{
Debug.Log("下按");
//state true:按下 false:松开
if (state)
DoSpeedUp();
else
DoSpeedRestore();
};
UIEventListener.Get(BtnLeft).onClick = go =>
{
Debug.Log("左移");
DoLeft();
};
UIEventListener.Get(BtnRight).onClick = go =>
{
Debug.Log("右移");
DoRight();
};
this.DoStart();
}
void ScoreChange()
{
ScoreLabel.text = "获得积分:" + this.Score.ToString();
}
void LineCountChange()
{
LineCountLabel.text = "消灭行数:" + this.LineCount.ToString();
}
void GameOverChange(bool state)
{
if (state)
EndPanel.SetActive(true);
}
/// <summary>
/// 初始化10*16的格子
/// </summary>
void DoInit()
{
this.LineCount = 0;
this.Score = 0;
curSpeed = 1;
for (int i = 0; i < aTable.Length; i++)
{
aTable[i] = new List<int>();
for (int j = 0; j < cCol; j++)
aTable[i].Add(0);
}
}
void OnGUI()
{
if (!bStart)
return;
if (BGameOver)
{
return;
}
if (bPause)
{
return;
}
for (int i = 0; i < curCell.Length; i++)
{
for (int j = 0; j < curCell[i].Length; j++)
{
if (curCell[i][j] == 0)
{
continue;
}
if (i + curCellRow < 0)
{
continue;
}
GUI.DrawTexture(new Rect(boxLeft + boxSize * j + curCellCol * boxSize, boxTop + boxSize * i + curCellRow * boxSize, boxSize, boxSize), BoxTexture[0]);
}
}
//背景图
for (int i = 0; i < aTable.Length; i++)
{
for (int j = 0; j < cCol; j++)
{
if (aTable[i][j] == 0)
{
continue;
}
GUI.DrawTexture(new Rect(boxLeft + boxSize * j, boxTop + boxSize * i, boxSize, boxSize), BoxTexture[aTable[i][j] - 1]);
}
}
}
string GetContent(int cellContent)
{
switch (cellContent)
{
case 0:
return "";
default:
return cellContent.ToString();
}
}
void DoStart()
{
DoInit();
DoNextCell();
bStart = true;
BGameOver = false;
}
void DrawNextBox()
{
//绘制下一个图
int boxIndex = 0;
for (int i = 0; i < nextCell.Length; i++)
{
for (int j = 0; j < nextCell[i].Length; j++)
{
if (nextCell[i][j] == 0)
{
continue;
}
BoxSprites[boxIndex++].transform.localPosition = new Vector3(j * 35, i * (-35), 0);
}
}
}
/// <summary>
/// 随机生成下面一个模型
/// </summary>
void DoNextCell()
{
if (nextCell == null)
{
nextCellID = rdm.Next(aCells.Length);
nextCell = aCells[nextCellID];
}
curCellCol = cCol / 2 - 2;
curCellRow = -4;
curCell = nextCell;
curCellID = nextCellID;
nextCellID = rdm.Next(aCells.Length);
nextCell = aCells[nextCellID];
DrawNextBox();
DetectIsFail();
}
/// <summary>
/// 放置当前的模型
/// </summary>
private void DoSetCurCellDown()
{
for (int i = 0; i < curCell.Length; i++)
{
for (int j = 0; j < curCell[i].Length; j++)
{
if (curCell[i][j] == 0)
{
continue;
}
if (curCellRow + i < 0)
{
BGameOver = true;
return;
}
aTable[curCellRow + i][curCellCol + j] = curCell[i][j];
}
}
//速度重置
DoSpeedRestore();
//消除一行
DoLine();
//生成下一个模型
DoNextCell();
}
void Update()
{
if (!bStart)
{
return;
}
if (BGameOver)
{
return;
}
if (bPause)
{
return;
}
//自动下滑
fDeltaTime += Time.deltaTime;
if (fDeltaTime > fCurSpeed)
{
fDeltaTime -= fCurSpeed;
if (CanMoveTo(curCellCol, curCellRow + 1))
{
curCellRow++;
}
else
{
DoSetCurCellDown();
return;
}
}
if (BGameOver)
{
return;
}
if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.LeftArrow)) //左移
{
DoLeft();
}
if (Input.GetKeyDown(KeyCode.D) || Input.GetKeyDown(KeyCode.RightArrow)) //右移
{
DoRight();
}
if (Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.UpArrow)) //上翻
{
DoTransform();
}
if (Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.DownArrow)) //按下快速下移
{
DoSpeedUp();
}
if (Input.GetKeyUp(KeyCode.S) || Input.GetKeyUp(KeyCode.DownArrow)) //按键取消 取消快速下移
{
DoSpeedRestore();
}
}
/// <summary>
/// 检测游戏是否结束
/// </summary>
private void DetectIsFail()
{
if (CanMoveTo(curCellCol, curCellRow))
{
return;
}
BGameOver = true;
}
/// <summary>
/// 判断模型移动
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="cell"></param>
/// <returns></returns>
bool CanMoveTo(int x, int y, int[][] cell = null) // 3, -4
{
if (cell == null)
{
cell = curCell;
}
for (int i = 0; i < cell.Length; i++)
{
for (int j = 0; j < cell[i].Length; j++)
{
if (cell[i][j] == 0)
{
continue;
}
if (x + j < 0 || x + j >= cCol)
{
return false;
}
if (y + i >= cRow) //到顶了
{
return false;
}
if (y + i < 0)
{
continue;
}
if (aTable[i + y][j + x] != 0)
{
return false;
}
}
}
return true;
}
private void DoSpeedRestore()
{
fCurSpeed = curSpeed;
}
//下移加速
private void DoSpeedUp()
{
fCurSpeed = curSpeed / 8;
fDeltaTime = fCurSpeed;
}
//右移
private void DoRight()
{
if (CanMoveTo(curCellCol + 1, curCellRow))
{
curCellCol++;
}
}
//左移
private void DoLeft()
{
if (CanMoveTo(curCellCol - 1, curCellRow))
{
curCellCol--;
}
}
//上翻
private void DoTransform()
{
int transformedCellID = curCellID / 4 * 4 + (curCellID % 4 + 1) % 4;
if (CanMoveTo(curCellCol, curCellRow, aCells[transformedCellID]))
{
curCellID = transformedCellID;
curCell = aCells[transformedCellID];
}
}
/// <summary>
/// 消除一行
/// </summary>
private void DoLine()
{
List<int> fulledLines = new List<int>();
for (int i = curCellRow; i < curCellRow + 4; i++)
{
if (i >= cRow)
{
continue;
}
if (i < 0)
{
BGameOver = true;
return;
}
for (int j = 0; j < cCol; j++)
{
if (aTable[i][j] == 0)
{
break;
}
if (j == cCol - 1)
{
fulledLines.Add(i);
}
}
}
if (fulledLines.Count == 0)
{
return;
}
for (int i = 0; i < fulledLines.Count; i++)
{
for (int j = 0; j < cCol; j++)
{
aTable[fulledLines[i]][j] = 0;
}
}
int ilastEmptyRow = fulledLines[fulledLines.Count - 1];
int ilastNotEmptyRow = ilastEmptyRow - 1;
while (true)
{
if (ilastNotEmptyRow < 0)
{
break;
}
if (!IsRowEmpty(ilastNotEmptyRow))
{
for (int j = 0; j < cCol; j++)
{
aTable[ilastEmptyRow][j] = aTable[ilastNotEmptyRow][j];
aTable[ilastNotEmptyRow][j] = 0;
}
ilastEmptyRow--;
}
ilastNotEmptyRow--;
}
for (int i = 0; i < fulledLines.Count; i++)
{
for (int j = 0; j < cCol; j++)
{
aTable[i][j] = 0;
}
}
//消除的行数
LineCount += fulledLines.Count;
//分数加成
Score += fulledLines.Count * fulledLines.Count;
curSpeed *= 0.99f;
}
bool IsRowEmpty(int irow)
{
for (int j = 0; j < cCol; j++)
{
if (aTable[irow][j] != 0)
{
return false;
}
}
return true;
}
/// <summary>
/// 各种形态的砖块 28个
/// </summary>
private int[][][] aCells = new int[][][]
{
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,1},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{0,1,1,0},
new int[]{0,0,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,1},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{0,1,1,0},
new int[]{0,0,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,0,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,0},
new int[]{0,1,1,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,0,0},
new int[]{0,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,0},
new int[]{0,1,1,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,1,1},
new int[]{0,0,0,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,1,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,1,1},
new int[]{0,0,0,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,1,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{1,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{0,1,1,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,0,0},
new int[]{1,1,1,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{1,1,0,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,0},
new int[]{1,1,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,0,0},
new int[]{0,1,0,0},
new int[]{0,1,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,1,0},
new int[]{1,0,0,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,1,1,0},
new int[]{0,1,0,0},
new int[]{0,1,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,1,1,0},
new int[]{0,0,1,0},
new int[]{0,0,0,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{0,0,1,0},
new int[]{0,0,1,0},
new int[]{0,1,1,0},
},
new int[][]
{
new int[]{0,0,0,0},
new int[]{1,0,0,0},
new int[]{1,1,1,0},
new int[]{0,0,0,0},
},
};
}
欢迎加群交流
1.unity游戏开发群
2.专门探讨XLua的程序群:437645698
下载地址
https://git.oschina.net/dingxiaowei/Aladdin_XLua.git
Github同步:https://github.com/dingxiaowei/Aladdin_XLua
关注后续更新请点start或者fork,感谢!
[Unity XLua]热更新XLua入门(二)-俄罗斯方块实例篇
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。