很久一段時間都沒有新文章了, 因為前一陣子一直在忙著做iPhone的遊戲, 引擎也就很少進展了. 但是由於人力, 以及時間等等因素, 案子進展的的很不順利. 接著六月開始我又得一個人帶小孩, 上下學接送, 洗澡樣樣得自己來所以忙到連iPhone遊戲的案子都沒時間作了. 於是只能趁小孩睡覺後的一兩個小時, 試試引擎的script system. 其實引擎的script系統早就玩成了, 之前是使用Lua為主, 但是從來沒用script來做過任何遊戲demo, 所以這一兩個小時的時間就正好用來試試script系統以及補強一些功能.
在修改了一些功能後, LnxEngine可以僅依靠Lua script完成整個遊戲的介面以及遊戲gameplay. 這次試作了一個橫向卷軸遊戲, 下面是遊戲的影片, 背景都是2D的sprite, 只有人物是3D的. 之後會繼續作作其他類型遊戲來試試看script system的能力.
之後上網看了一下Mono, 才發現Mono runtime 其實可以直接內嵌到應用程式中, 所以任何符合.Net 的bytecode, 就可以藉由Mono runtime來執行. 花了幾天時間上網查了一些資料, 發現整合Mono沒想像中的難, 不過原本的 C#+C++/CLI+Native的作法在Mono上卻行不通於是只好改用類似swig的作法來橋接Managed code跟Native code, 缺點是要花多一點時間來寫wrapper. 之後使用了C#來完成了之前用Lua作出的卷軸遊戲. 使用Mono最大的好處是, 任何能編譯成.Net bytecode的語言都可以用來作為引擎的script語言, C#, VB, JavaScript, Java都沒問題, 甚至Lua都有人作出bytecode編譯器, 所以彈性可以說是非常的高. 目前Lua與Mono是併存於LynxEngine中, 但將來應該會慢慢轉移到以Mono為主.
以下就列出C#以及Lua的code 作個比較, 相較起來Mono還是更有彈性.
C# script list
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using LynxEngine;
//----------------------------------------------
// 說明:
//----------------------------------------------
public class CBGLayer
{
public float ObjectWidth, ObjectHeight;
public uint ObjectInterval;
public float PosY;
public uint NumObjects;
public float ScrollingSpeed;
public CDynamicObj[] Objects = null;
public float[] Pos = null;
public CBGLayer()
{
}
public void CreateObjects(
CScene scene,
int n,
String name,
String filename,
CRenderableObj.DEPTHLAYERTYPE dl)
{
Objects = new CDynamicObj[n];
Pos = new float[n];
for (uint i=0; i < Objects.GetLength(0); i++)
{
Objects[i] =
scene.CreateDynamicEntity();
Objects[i].CreateSprite(
name,
filename,
ObjectWidth,
ObjectHeight);
Objects[i].UpdateTransform();
Objects[i].SetDepthLayer(dl);
scene.AddDynamicObj(Objects[i]);
}
}
}
//----------------------------------------------
// 說明:
//----------------------------------------------
class CPlayer : CDynamicObj
{
public enum STATE
{
RUN = 0,
JUMP,
ATTACK1
};
public class CState
{
public CSound Sound = null;
public CAnimation Animation = null;
}
public STATE State;
public CState [] States = null;
public CPlayer(IntPtr ptr)
: base(ptr)
{
}
public void SetState(STATE state)
{
States[(uint)State].Sound.Stop();
SetCurrentAnimation(
States[(uint)state].Animation);
States[(uint)state].Sound.Play();
State = state;
}
}
//----------------------------------------------
// 說明:
//----------------------------------------------
public class CScrollingGamePage : CUIPage
{
bool GameConsoleIsRunning = false;
CBGLayer[] BGLayers = new CBGLayer[4];
CPlayer Player;
CSound BGM;
CVector2 [] BackgroundUV = new CVector2 [2];
CScene Scene;
new public void OnCreate()
{
Scene = GlobalVar.SceneSystem.AddScene();
Scene.SetName("Scrolling Game Scene") ;
CCamera Camera = Scene.CreateCamera();
Camera.Create();
Scene.SetCurrentCamera(Camera);
CCameraContainer CameraContainer =
(CCameraContainer)Camera.GetContainer();
CVector3 CameraPos =
new CVector3(10, 10, -35);
CameraContainer.SetPosition(CameraPos);
CVector3 CameraLookPos =
new CVector3(10, 10, 0);
CameraContainer.LookAt(CameraLookPos);
CameraContainer.UpdateProjectionMatrix(
(float)(GlobalVar.GraphicsSystem.GetBackbufferWidth()) /
(float)(GlobalVar.GraphicsSystem.GetBackbufferHeight()));
CameraContainer.UpdateViewMatrix();
Scene.LoadBackgroundTexture(
"../texture/scene/map00/background0.tga");
BackgroundUV[0] = new CVector2();
BackgroundUV[1] = new CVector2();
BackgroundUV[0].x = 0.0f; BackgroundUV[0].y = 0.0f;
BackgroundUV[1].x = -0.5f; BackgroundUV[1].y = 0.0f;
Scene.SetBackgroundTextureUVOffset(
BackgroundUV[0].x, BackgroundUV[0].y,
BackgroundUV[1].x, BackgroundUV[1].y);
Player = new CPlayer(
Scene.CreateDynamicEntity().GetPtr());
Player.CreateModel(
"Player",
"../model/dynobj/boy/boy.mdl");
Player.SetDepthLayer(
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_3);
Player.Yaw(-90.0f, MATHORDER.LYNX_MATH_POST);
Scene.AddDynamicObj((CDynamicObj)Player);
Player.States = new CPlayer.CState[3];
Player.States[(uint)CPlayer.STATE.RUN] =
new CPlayer.CState();
Player.States[(uint)CPlayer.STATE.RUN].Animation =
Player.LoadAnimation("../model/dynobj/boy/run.ani");
Player.States[(uint)CPlayer.STATE.RUN].Sound =
GlobalVar.SoundSystem.LoadSound(
"Run",
"../sound/run.wav");
Player.States[(uint)CPlayer.STATE.RUN].Sound.SetLoops(-1);
Player.States[(uint)CPlayer.STATE.JUMP] =
new CPlayer.CState();
Player.States[(uint)CPlayer.STATE.JUMP].Animation =
Player.LoadAnimation("../model/dynobj/boy/jump.ani");
Player.States[(uint)CPlayer.STATE.JUMP].Sound =
GlobalVar.SoundSystem.LoadSound(
"Jump",
"../sound/jump.wav");
Player.States[(uint)CPlayer.STATE.ATTACK1] =
new CPlayer.CState();
Player.States[(uint)CPlayer.STATE.ATTACK1].Animation =
Player.LoadAnimation("../model/dynobj/boy/attack1.ani");
Player.States[(uint)CPlayer.STATE.ATTACK1].Sound =
GlobalVar.SoundSystem.LoadSound(
"Attack1",
"../sound/attack1.wav");
BGLayers[0] = new CBGLayer();
BGLayers[0].ObjectWidth = 160;
BGLayers[0].ObjectHeight = 320;
BGLayers[0].ObjectInterval = 110;
BGLayers[0].PosY = 320-35-320;
BGLayers[0].ScrollingSpeed = 1;
BGLayers[0].CreateObjects(
Scene, 6,
"Tree",
"../texture/scene/map00/tree_23.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_5);
BGLayers[1] = new CBGLayer();
BGLayers[1].ObjectWidth = 256;
BGLayers[1].ObjectHeight = 256;
BGLayers[1].ObjectInterval = 256;
BGLayers[1].PosY = 320-35-256;
BGLayers[1].ScrollingSpeed = 2.5f;
BGLayers[1].CreateObjects(
Scene, 3,
"Tree2",
"../texture/scene/map00/tree_01.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_4);
BGLayers[2] = new CBGLayer();
BGLayers[2].ObjectWidth = 128;
BGLayers[2].ObjectHeight = 64;
BGLayers[2].ObjectInterval = 110;
BGLayers[2].PosY = 320 - 22 - 64;
BGLayers[2].ScrollingSpeed = 6.0f;
BGLayers[2].CreateObjects(
Scene, 6,
"Bush",
"../texture/scene/map00/bush_08.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_3);
BGLayers[3] = new CBGLayer();
BGLayers[3].ObjectWidth = 64;
BGLayers[3].ObjectHeight = 64;
BGLayers[3].ObjectInterval = 64;
BGLayers[3].PosY = 320 - 40;
BGLayers[3].ScrollingSpeed = 6.0f;
BGLayers[3].CreateObjects(
Scene, 9,
"GroundTile",
"../texture/scene/map00/ground.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_2);
BGM = GlobalVar.SoundSystem.LoadSound(
"BGM",
"../sound/BGM.wav");
BGM.SetLoops(-1);
Scene.SetRenderMode(CScene.RENDERMODE.RENDER_SIMPLE);
Scene.SetSortMode(CScene.SORTMODE.SORT_BY_DEPTH_LAYER);
Scene.Setup();
}
//----------------------------------------------
// 說明:
//----------------------------------------------
new public void OnInit()
{
GlobalVar.SystemMouse =
(CMouse)(GlobalVar.InputSystem.FindDevice("System Mouse"));
GlobalVar.Engine.DeleteLastUIPage();
Player.SetState(CPlayer.STATE.RUN);
for (int i = 0; i < BGLayers.GetLength(0); i++)
{
for (int j=0; j < BGLayers[i].Objects.GetLength(0); j++)
{
BGLayers[i].Pos[j] = (int)(j*BGLayers[i].ObjectInterval);
}
}
BGM.SetVolume(0.6f);
BGM.Play();
}
//----------------------------------------------
// 說明:
//----------------------------------------------
void AnimationLoop()
{
if (GlobalVar.SystemMouse.ButtonStatus(CMouse.RBUTTON) &&
Player.State == CPlayer.STATE.RUN)
{
Player.SetState(CPlayer.STATE.JUMP);
}
if (GlobalVar.SystemMouse.ButtonStatus(CMouse.LBUTTON) &&
Player.State == CPlayer.STATE.RUN)
{
Player.SetState(CPlayer.STATE.ATTACK1);
}
if (Player.State != CPlayer.STATE.RUN)
{
if (Player.IsCurrentAnimationStopped())
{
Player.SetState(CPlayer.STATE.RUN);
}
}
}
//----------------------------------------------
// 說明:
//----------------------------------------------
new public void OnLoop(float step)
{
GlobalVar.SystemMouse.Poll();
if (GlobalVar.GameConsoleSystem.IsRunning())
{
GameConsoleIsRunning = true;
Player.States[(int)Player.State].Sound.Pause();
}
else
{
AnimationLoop();
if (GameConsoleIsRunning)
{
if (!Player.States[(int)Player.State].Sound.IsPlaying())
{
Player.States[(int)Player.State].Sound.Play();
}
GameConsoleIsRunning = false;
}
if (Player.State == CPlayer.STATE.RUN ||
Player.State == CPlayer.STATE.JUMP)
{
BackgroundUV[0].x += (step * 0.0003f);
BackgroundUV[1].x += (step * 0.0003f);
Scene.SetBackgroundTextureUVOffset(
BackgroundUV[0].x, BackgroundUV[0].y,
BackgroundUV[1].x, BackgroundUV[1].y);
for (int i = 0; i < BGLayers.GetLength(0); i++)
{
for (int j = 0;
j < BGLayers[i].Objects.GetLength(0); j++)
{
BGLayers[i].Pos[j] -=
(BGLayers[i].ScrollingSpeed * step);
if (BGLayers[i].Pos[j] < -BGLayers[i].ObjectWidth)
BGLayers[i].Pos[j] +=
(BGLayers[i].Objects.GetLength(0)*
BGLayers[i].ObjectInterval);
CVector3 Pos =
new CVector3(BGLayers[i].Pos[j],
BGLayers[i].PosY,
0);
BGLayers[i].Objects[j].SetPosition(Pos);
BGLayers[i].Objects[j].UpdateTransform();
}
}
}
}
}
//----------------------------------------------
// 說明:
//----------------------------------------------
new public void OnRender()
{
GlobalVar.Engine.Render();
}
//----------------------------------------------
// 說明:
//----------------------------------------------
new public void OnQuit()
{
base.OnQuit();
}
}
Lua script list
Player = {DynObj = 0, State = 0, States = {}}
Player.STATE = {}
Player.STATE.RUN = 0
Player.STATE.JUMP = 1
Player.STATE.ATTACK1 = 2
Scene = nil
BGLayer = {}
BackgroundUV = {u0 = 0.0, v0 = 0.0, u1 = -0.51, v1 = 0.0}
BGM = nil
GameConsoleIsRunning = false
------------------------------------------------
-- 說明:
------------------------------------------------
Player.SetState = function (state)
CSound_Cast(Player.States[Player.State].Sound):Stop()
CDynamicObj_Cast(Player.DynObj):SetCurrentAnimation(
Player.States[state].Animation)
CSound_Cast(Player.States[state].Sound):Play()
Player.State = state
end
------------------------------------------------
-- 說明:
------------------------------------------------
function BGLayer_Create (scene, layer, n, name, filename, depthlayer)
BGLayer[layer].NumObjects = n
for i=0,BGLayer[layer].NumObjects-1 do
DynamicObj = CDynamicObj_Cast(scene:CreateDynamicEntity())
DynamicObj:CreateSprite(
name,
filename,
BGLayer[layer].ObjectWidth,
BGLayer[layer].ObjectHeight)
DynamicObj:UpdateTransform()
DynamicObj:SetDepthLayer(depthlayer)
scene:AddDynamicObj(GetObjectPointer(DynamicObj))
BGLayer[layer][i] = {}
BGLayer[layer][i].DynObj = GetObjectPointer(DynamicObj)
BGLayer[layer][i].Pos = i*BGLayer[layer].ObjectWidth
end
end
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.OnCreate = function ()
CSampleScriptGame:ComputeScreenRatio()
Scene = CScene_Cast(CSceneSystem:AddScene())
Scene:SetName(L"Scrolling Game Scene")
Camera = CCamera_Cast(Scene:CreateCamera())
Camera:Create()
Scene:SetCurrentCamera(GetObjectPointer(Camera))
CameraContainer = CCameraContainer_Cast(Camera:GetContainer())
CameraContainer:SetPosition(CVector3(10, 10, -35))
CameraContainer:LookAt(10, 10, 0)
CameraContainer:UpdateProjectionMatrix(
CGraphicsSystem:GetBackbufferWidth()/
CGraphicsSystem:GetBackbufferHeight())
CameraContainer:UpdateViewMatrix()
Scene:LoadBackgroundTexture(
L"../texture/scene/map00/background0.tga")
Scene:SetBackgroundTextureUVOffset(
BackgroundUV.u0, BackgroundUV.v0,
BackgroundUV.u1, BackgroundUV.v1)
DynamicObj = CDynamicObj_Cast(Scene:CreateDynamicEntity())
DynamicObj:CreateModel(L"Player", L"../model/dynobj/boy/boy.mdl")
DynamicObj:SetDepthLayer(CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_3)
DynamicObj:Yaw(-90.0, 1)
Scene:AddDynamicObj(GetObjectPointer(DynamicObj))
Player.States[Player.STATE.RUN] = {}
Player.States[Player.STATE.RUN].Animation =
DynamicObj:LoadAnimation(L"../model/dynobj/boy/run.ani")
Player.States[Player.STATE.RUN].Sound =
CSoundSystem:LoadSound(L"Run", L"../sound/run.wav")
CSound_Cast(Player.States[Player.STATE.RUN].Sound):SetLoops(-1)
Player.DynObj = GetObjectPointer(DynamicObj)
Player.States[Player.STATE.JUMP] = {}
Player.States[Player.STATE.JUMP].Animation =
DynamicObj:LoadAnimation(L"../model/dynobj/boy/jump.ani")
Player.States[Player.STATE.JUMP].Sound =
CSoundSystem:LoadSound(L"Jump", L"../sound/jump.wav")
Player.States[Player.STATE.ATTACK1] = {}
Player.States[Player.STATE.ATTACK1].Animation =
DynamicObj:LoadAnimation(L"../model/dynobj/boy/attack1.ani")
Player.States[Player.STATE.ATTACK1].Sound =
CSoundSystem:LoadSound(L"Attack1", L"../sound/attack1.wav")
BGLayer[0] = {}
BGLayer[0].ObjectWidth = 160
BGLayer[0].ObjectHeight = 320
BGLayer[0].ObjectInterval = 110
BGLayer[0].PosY = 320-35-320
BGLayer[0].ScrollingSpeed = 1
BGLayer_Create(
Scene, 0, 6,
L"Tree",
L"../texture/scene/map00/tree_23.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_5)
BGLayer[1] = {}
BGLayer[1].ObjectWidth = 256
BGLayer[1].ObjectHeight = 256
BGLayer[1].ObjectInterval = 256
BGLayer[1].PosY = 320-35-256
BGLayer[1].ScrollingSpeed = 2.5
BGLayer_Create(
Scene, 1, 3,
L"Tree2",
L"../texture/scene/map00/tree_01.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_4)
BGLayer[2] = {}
BGLayer[2].ObjectWidth = 128
BGLayer[2].ObjectHeight = 64
BGLayer[2].ObjectInterval = 110
BGLayer[2].PosY = 320-22-64
BGLayer[2].ScrollingSpeed = 6
BGLayer_Create(
Scene, 2, 6,
L"Bush",
L"../texture/scene/map00/bush_08.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_1)
BGLayer[3] = {}
BGLayer[3].ObjectWidth = 64
BGLayer[3].ObjectHeight = 64
BGLayer[3].ObjectInterval = 64
BGLayer[3].PosY = 320-40
BGLayer[3].ScrollingSpeed = 6
BGLayer_Create(
Scene, 3, 9,
L"GroundTile",
L"../texture/scene/map00/ground.tga",
CRenderableObj.DEPTHLAYERTYPE.DEPTH_LAYER_1)
BGM = (CSoundSystem:LoadSound(L"BGM", L"../sound/BGM.wav"))
CSound_Cast(BGM):SetLoops(-1)
Scene:SetRenderMode(CScene.RENDERMODE.RENDER_SIMPLE)
Scene:SetSortMode(CScene.SORTMODE.SORT_BY_DEPTH_LAYER)
Scene:Setup()
end
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.OnInit = function ()
CEngine:DeleteLastUIPage()
Player.SetState(Player.STATE.RUN)
for i=0,3 do
for j=0, BGLayer[i].NumObjects-1 do
BGLayer[i][j].Pos = j*BGLayer[i].ObjectInterval
end
end
CSound_Cast(BGM):SetVolume(0.6)
CSound_Cast(BGM):Play()
end
------------------------------------------------
-- 說明:
------------------------------------------------
MouseDevice = CMouse_Cast(CInputSystem:FindDevice(L"System Mouse"))
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.AnimationLoop = function ()
if (MouseDevice:ButtonStatus(RBUTTON) == TRUE and
Player.State == Player.STATE.RUN) then
Player.SetState(Player.STATE.JUMP)
end
if (MouseDevice:ButtonStatus(LBUTTON) == TRUE and
Player.State == Player.STATE.RUN) then
Player.SetState(Player.STATE.ATTACK1)
end
if (Player.State ~= Player.STATE.RUN) then
if (CDynamicObj_Cast(
Player.DynObj):IsCurrentAnimationStopped() == TRUE) then
Player.SetState(Player.STATE.RUN)
end
end
end
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.OnLoop = function (step)
MouseDevice:Poll()
if (CGameConsoleSystem:IsRunning() == TRUE) then
GameConsoleIsRunning = true
CSound_Cast(Player.States[Player.State].Sound):Pause()
else
CScrollingGamePage:AnimationLoop()
if (GameConsoleIsRunning == true) then
if (CSound_Cast(
Player.States[Player.State].Sound):IsPlaying() == FALSE) then
CSound_Cast(Player.States[Player.State].Sound):Play()
end
GameConsoleIsRunning = false
end
if (Player.State == Player.STATE.RUN or
Player.State == Player.STATE.JUMP) then
BackgroundUV.u0 = BackgroundUV.u0 + (step * 0.0003)
BackgroundUV.u1 = BackgroundUV.u1 + (step * 0.0003)
Scene:SetBackgroundTextureUVOffset(
BackgroundUV.u0, BackgroundUV.v0,
BackgroundUV.u1, BackgroundUV.v1)
for i=0,3 do
for j=0, BGLayer[i].NumObjects-1 do
BGLayer[i][j].Pos =
BGLayer[i][j].Pos - BGLayer[i].ScrollingSpeed*step
if (BGLayer[i][j].Pos < -BGLayer[i].ObjectWidth) then
BGLayer[i][j].Pos =
BGLayer[i][j].Pos +
BGLayer[i].NumObjects*BGLayer[i].ObjectInterval
end
DynObj = CDynamicObj_Cast(BGLayer[i][j].DynObj)
DynObj:SetPosition(
CVector3(BGLayer[i][j].Pos, BGLayer[i].PosY, 0))
DynObj:UpdateTransform()
end
end
end
end
end
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.OnRender = function ()
CEngine:Render()
end
------------------------------------------------
-- 說明:
------------------------------------------------
CScrollingGamePage.OnQuit = function ()
CUIPage:OnQuit();
end
3 comments:
LynxEngine越來越強了! Mono和Lua在速度及memory footprint上比較起來差別如何?
Lua基本上memory footprint還是比較有優勢的, 但是以執行速度來說, Mono就比Lua快上許多(PC version, 5xx fps v.s. 3xx fps). Mono比較大的問題是iOS, Android並無免費版本, 必須購買Mono Touch才能執行Mono runtime,這點不知Unity是如何解決的?
一般Unity/Unity Pro license並沒有包含Android或iOS的支援, 要另外多付US$400才有.
MonoTouch Pro的定價是US$399, 我猜Unity那個$400就是付MonoTouch的license fee.
Post a Comment