怎么在unity3d中将一个球添加在主unity 相机旋转下的public

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
using&UnityE&&using&S&&using&System.C&&using&System.D&&using&MySql.Data.MySqlC&&public&class&CMySql&:&MonoBehaviour&{&&&&&&//&Global&variables&&&&&&public&static&MySqlConnection&dbC//Just&like&MyConn.conn&in&StoryTools&before&&&&&&&static&string&host&=&"192.168.1.100";&&&&&&&static&string&id&=&"mysql";//这里是你自己的数据库的用户名字,我一开始想用root,发现不行,后来添加了新的用户才可以&&&&&&&static&string&pwd&=&"123456";&&&&&&&static&string&database&=&"test";&&&&&&&static&string&result&=&"";&&&&&&&&private&string&strCommand&=&"Select&*&from&unity3d_test&ORDER&BY&";&&public&static&DataSet&MyO&&&&&&&void&OnGUI()&&&&&&&{&&&&&&&&&&&host&=&GUILayout.TextField(&host,&200,&GUILayout.Width(200));&&&&&&&&&&&id&=&GUILayout.TextField(&id,&200,&GUILayout.Width(200));&&&&&&&&&&&pwd&=&GUILayout.TextField(&pwd,&200,&GUILayout.Width(200));&&&&&&&&&&&if(GUILayout.Button("Test"))&&&&&&&&&&&{&&&&&&string&connectionString&=&string.Format("Server&=&{0};&Database&=&{1};&User&ID&=&{2};&Password&=&{3};",host,database,id,pwd);&&&&&&openSqlConnection(connectionString);&&&&&&&&&&&&MyObj&=&GetDataSet(strCommand);&&&&&&&&&&&}&&&&&&&&&&&&GUILayout.Label(result);&&&&&&&}&&&&&&&&//&On&quit&&&&&&public&static&void&OnApplicationQuit()&{&&&&&&&&&&closeSqlConnection();&&&&&&}&&&&&&&&&&&//&Connect&to&database&&&&&&private&static&void&openSqlConnection(string&connectionString)&{&&&&&&&&&&dbConnection&=&new&MySqlConnection(connectionString);&&&&&&&&&&dbConnection.Open();&&&&&&&&&&result&=&dbConnection.ServerV&&&&&&&&&&//Debug.Log("Connected&to&database."+result);&&&&&&}&&&&&&&&&&&//&Disconnect&from&database&&&&&&private&static&void&closeSqlConnection()&{&&&&&&&&&&dbConnection.Close();&&&&&&&&&&dbConnection&=&null;&&&&&&&&&&//Debug.Log("Disconnected&from&database."+result);&&&&&&}&&&&&&&&&&&&//&MySQL&Query&&&&&&public&static&void&doQuery(string&sqlQuery)&{&&&&&&&&&&IDbCommand&dbCommand&=&dbConnection.CreateCommand();&&&&&&&&&&&&&&mandText&=&sqlQ&&&&&&&&&&IDataReader&reader&=&dbCommand.ExecuteReader();&&&&&&&&&&reader.Close();&&&&&&&&&&reader&=&null;&&&&&&&&&&dbCommand.Dispose();&&&&&&&&&&dbCommand&=&null;&&&&&&}&&&&&&#region&Get&DataSet&&&&&&public&&DataSet&GetDataSet(string&sqlString)&&&&&&{&&&&&&&&&&//string&sql&=&UnicodeAndANSI.UnicodeAndANSI.UnicodeToUtf8(sqlString);&&&&&&&&&&&&DataSet&ds&=&new&DataSet();&&&&&&&&&&try&&&&&&&&&&{&&&&&&&&&&&&&&MySqlDataAdapter&da&=&new&MySqlDataAdapter(sqlString,&dbConnection);&&&&&&&&&&&&&&da.Fill(ds);&&&&&&&&&&&&&&&}&&&&&&&&&&catch&(Exception&ee)&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&throw&new&Exception("SQL:"&+&sqlString&+&"/n"&+&ee.Message.ToString());&&&&&&&&&&}&&&&&&&&&&return&&&&&&&&&&&}&&&&&&#endregion&&&}&&& C#代码: [c-sharp] using&UnityE&&using&S&&using&System.C&&using&System.D&&public&class&DataBaseTest&:&MonoBehaviour&{&&public&GUISkin&myGUISkin&=&new&GUISkin();&&string&strID&=&"";&&string&strName&=&"";&&string&strSex&=&"";&&int&Index&=&1;&&//&Use&this&for&initialization&&void&Start&()&{&&}&&void&OnGUI()&&{&&&&GUI.skin&=&myGUIS&&&&if&(GUI.Button(new&Rect(100,320,100,100),"Click&Me"))&&&&{&&&&&foreach(DataRow&dr&in&CMySql.MyObj.Tables[0].Rows)&&&&&{&&&&&&if&(Index.ToString()&==&dr["ID"].ToString())&&&&&&{&&&&&&&strID&=&dr["ID"].ToString();&&&&&&&strName&=&&dr["Name"].ToString();&&&&&&&strSex&=&dr["Sex"].ToString();&&&&&&&&&&&&&&break;&&&&&&}&&&&&}&&&&&&&&Index++;&&&&&&if(Index&&&5)&&&&&{&&&&&&Index&=&1;&&&&&&}&&&&&&&&&&&}&&&&GUI.Label(new&Rect(320,100,150,70),"DataBaseTest");&&&&GUI.Label(new&Rect(300,210,150,70),strID);&&&&GUI.Label(new&Rect(300,320,150,70),strName);&&&&GUI.Label(new&Rect(300,430,150,70),strSex);&&&&&&}&&}&&& 2.導入dll&&同先前的帖子 , 將MySql.data.dll Import至Assets底下 , 然後再到Unity/Editor/Data/Frameworks/Mono.framework 中將System.Data.dll 也一起Import至Assets內 , 當然 , 如果想顯示中文的話 , 請參考中文視頻教學 , 建立一個GUISkin與字型3.建立數據庫內容&&主要是因為代碼中的這段內容& &&&static string host = "192.168.1.100";& &&&static string id = "mysql";& &&&static string pwd = "123456";& &&&static string database = "test";& &&&private string strCommand = "Select * from unity3d_test ORDER BY";其中host ,id , pwd 請自行設定 , 簡單的說就是連進你的MySQL啦~然後建立一個名為test的Database , 在這個test下建立一張table , 取名為 unity3d_test ,接下來就為這張unity3d_test建立3個欄位 : ID , Name , Sex (記得將ID設定為primary key 且默認值為1)再來自行填入5筆資料(5筆資料的原因是腳本那邊是設定成5筆資料一個循環 , 使用者可以自行更改腳本試試)4.建立GameObject&&建立完GameObject後將上面兩個腳本掛上去 , 如果有建立GUISkin , 記得指定GUISkin5.執行&&執行後先按Test按鈕來連接數據庫 , 然後再按"Click Me"來顯示數據庫內的內容6.總結&&&以上部份是有關連線至數據庫後 , 再將資料用DataSet的方式獲得出來再加以顯示至介面上 , 歡迎各位高手能夠多多批評指教
阅读(2665)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'在unity3d中连接数据库',
blogAbstract:'
1.C#代码: [c-sharp] '
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}程序写累了,就来玩玩酷跑小游戏吧,嘿嘿。
雨松MOMO送你一首歌曲,嘿嘿。
Unity3D研究院之平面小球重力感应详解(十三)
Unity3D研究院之平面小球重力感应详解(十三)
围观32042次
编辑日期: 字体:
手机重力感应应该对大多数开发者并不陌生,在新一代智能手机Android
IOS WP7 很多游戏都是使用手机自带重力感应功能制作的,强大的Unity3D 游戏引擎当然对这个也是完美支持的,今天由MOMO带大家学习3D 世界中的手机重力感应。本章我们的目标是实现一个小球在屏幕中通过摇晃手机重力加速度让小球在屏幕中移动。以前的Android系列开发文章中貌似也写过,其实原理都是一样一样一样的,废话不多说了。哇咔咔~~
先看一看Unity3D 在iPhone上的重力分布图。如下图所示我们可以清晰的看出X Y Z 三个方向的重力分量。Unity3D中重量的取值范围是 -1.0 到 +1.0.
X轴:home按键在下手机面朝天向右旋转90度重力分量为+1.0
向左旋转90度重力分量为-1.0
Y轴:home按键在上手机背朝自己重力分量为+1.0 home按键在下手机面朝自己重力分量为-1.0
Z轴:手机面朝地面重力分量为+1.0 手机面朝天空重力分量为-1.0
OK! 有了这三组重要的数值我们就可以控制手机重力感应啦,紧接着我们看看小球重力感应的这个游戏小例子。
打开Unity3D 我们将给摄像机绑定一个脚本,用来响应用户控制手机来重力感应游戏小球的移动。
在Input 这个重要的类中,Unity3D 帮我们封装了重力加速的方法。
Input.acceleration.x; 重力感应X轴的重力分量
Input.acceleration.y; 重力感应Y轴的重力分量
Input.acceleration.z; 重力感应Z轴的重力分量
123456789101112131415161718192021222324252627282930313233343536373839404142434445
//小球的贴图var round : Texture2D;&&&//小球在屏幕中显示的X Y坐标var x = 0;var y = 0;&//小球屏幕显示的最大 X Y 范围var cross_x = 0;var cross_y = 0;&function Start(){
//初始化赋值&&&&cross_x = Screen.width -&&round.width;&&&&&&cross_y = Screen.height -&&round.height;&& }&function OnGUI () {& //整体显示 x y z 重力感应的重力分量 GUI.Label(Rect(0,0,480,100),"position is " + Input.acceleration);& //绘制小球 GUI.DrawTexture(Rect(x,y,256,256),round);&& }&function Update(){& //根据重力分量修改小球的位置这里乘以30的意思是让小球移动的快一些 x += Input.acceleration.x * 30; y += -Input.acceleration.y * 30; & //避免小球超出屏幕 if(x & 0){&&&&&&&&&&x = 0;&&&&&&}else if(x & cross_x){&&&&&&&&&&x = cross_x;&&&&&&}&&&&&&&if(y & 0){&&&&&&&&&&y = 0;&&&&&&}else if(y & cross_y){&&&&&&&&&&y = cross_y;&&&&&&}&&}
重力感应的图片不太好截取,我们看看下面的示意图,小球可以根据我的手机的重力而移动。屏幕左上方正常打印手机当前X Y Z 三个方向的重力分量。
说到这里,可能会有盆友问我如何为3D的物体添加重力感应?其实方法是一样的,因为都是一样的道理,比如我可以给一个模型绑定一个这样的脚本,通过Input.acceleration去拿到当前手机的重力感应分量,然后根据这个分量去计算当前模型的位置。细心的盆友你们可以试一试,其实很简单的哇咔咔~~
最后欢迎各位盆友可以和MOMO一起讨论Unity3D游戏开发,总的来说这一章还是比较简单的,哇咔咔~~~ 附上Unity3D工程的下载地址,Xcode项目我就不上传了,须要的自己导出。
下载地址:
本文固定链接:
转载请注明:
雨松MOMO提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
您可能还会对这些文章感兴趣!Unity3D中实现帧同步(Part 1和Part 2)
招聘信息:
第一部分在帧同步模型中,每个客户端都会对整个游戏世界进行模拟。这种方法的好处在于减少了需要发送的信息。帧同步只需要发送用户的输入信息,而对于反过来的中心服务器模型来说,单位的信息则发送越频繁越好。比如说你在游戏世界中移动角色。在中心服务器模型中,物理模拟只会在服务器执行。客户端告诉服务器,角色要往哪个方向移动。服务器会执行寻路而且开始移动角色。服务器紧接着就会尽可能频繁地告知每个客户端该角色的位置。对于游戏世界中的每个角色都要运行这样的过程。对于实时策略游戏来说,同步成千上万的单位在中心服务器模型中几乎是不可能的任务。在帧同步模型中,在用户决定移动角色之后,就会告诉所有客户端。每个客户端都会执行寻路以及更新角色位置。只有用户输入的时候才需要通知每个客户端,然后每个客户端都会自己更新物理以及位置。这个模型带来了一些问题。每个客户端的模拟都必须执行得一模一样。这意味着,物理模拟必须执行同样的更新次数而且每个动作都需要同样的顺序执行。如果不这么做,其中一个客户端就会跑在其他客户端之前或者之后,然后在新的命令发出之后,跑得太快或者太慢的客户端走出的路径就会不同。这些不同会根据不同的游戏玩法而不同。另一个问题就是跨不同的机器和平台的确定性问题。计算上很小的不同都会对游戏造成蝴蝶效应。这个问题会在后续的文章中讲到。这里的实现方案灵感来自于这篇文章:《》。每个玩家命令都会在后续的两个回合中执行。在发送动作与处理动作之间存在延迟有助于对抗网络延迟。这个实现还给我们留下了根据延迟以及机器性能动态调整每回合时长的空间。这部分在这里先不讨论,会在后续文章再说。对于这个实现,我们有如下定义:帧同步回合:帧同步回合可以由多个游戏回合组成。玩家在一个帧同步回合执行一个动作。帧同步回合长度会根据性能调整。目前硬编码为200ms。游戏回合:游戏回合就是游戏逻辑和物理模拟的更新。每个帧同步回合拥有的游戏回合次数是由性能控制的。目前硬编码为50ms,也就是每次帧同步回合有4次游戏回合。也就是每秒有20次游戏回合。动作:一个动作就是玩家发起的一个命令。比如说在某个区域内选中单位,或者移动选中单位到目的地。注意:我们将不使用unity3d的物理引擎。而是使用一个确定性的自定义引擎。在后续文章中会有实现。游戏主循环Unity3d的循环是运行在单线程下的。可以通过在这两个函数插入自定义代码:Update()
FixedUpdate()Unity3d的主循环每次遍历更新都会调用Update()。主循环会以最快速度运行,除非设置了固定的帧率。FixedUpdate()会根据设置每秒执行固定次数。在主循环遍历中,它会被调用零次或多次,取决于上次遍历所花费的时间。FixedUpdate()有着我们想要的行为,就是每次帧同步回合都执行固定时长。但是,FixedUpdate()的频率只能在运行之前设置好。而我们希望可以根据性能调节我们的游戏帧率。游戏帧回合这个实现有着与FixedUpdate()在Update()函数中执行所类似的逻辑。主要不同的地方在于,我们可以调整频率。这是通过增加”累计时间”来完成的。每次调用Update()函数,上次遍历所花费的时间会添加到其中。这就是Time.deltaTime。如果累计时间大于我们的固定游戏回合帧率(50ms),那么我们就会调用gameframe()。我们每次调用gameframe()都会在累计时间上减去50ms,所以我们一直调用,知道累计时间小于50ms。private&float&AccumilatedTime&=&0f;
private&float&FrameLength&=&0.05f;&//50&miliseconds
//called&once&per&unity&frame
public&void&Update()&{
&&&&//Basically&same&logic&as&FixedUpdate,&but&we&can&scale&it&by&adjusting&FrameLength
&&&&AccumilatedTime&=&AccumilatedTime&+&Time.deltaT
&&&&//in&case&the&FPS&is&too&slow,&we&may&need&to&update&the&game&multiple&times&a&frame
&&&&while(AccumilatedTime&>&FrameLength)&{
&&&&&&&&GameFrameTurn&();
&&&&&&&&AccumilatedTime&=&AccumilatedTime&-&FrameL
}我们跟踪当前帧同步回合中游戏帧的数量。每当我们在帧同步回合中达到我们想要的游戏回合次数,我们就会更新帧同步回合到下一轮。如果帧同步还不能到下一轮,我们就不能增加游戏帧,而且我们会在下一次同样执行帧同步检查。private&void&GameFrameTurn()&{
&&&&//first&frame&is&used&to&process&actions
&&&&if(GameFrame&==&0)&{
&&&&&&&&if(LockStepTurn())&{
&&&&&&&&&&&&GameFrame++;
&&&&}&else&{
&&&&&&&&//update&game
&&&&&&&&//...
&&&&&&&&GameFrame++;
&&&&&&&&if(GameFrame&==&GameFramesPerLocksetpTurn)&{
&&&&&&&&&&&&GameFrame&=&0;
}在游戏回合中,物理模拟会更新而且我们的游戏逻辑也会更新。游戏逻辑是通过接口(IHasGameFrame)来实现的,而且添加这个对象到集合中,然后我们就可以进行遍历。private&void&GameFrameTurn()&{
&&&&//first&frame&is&used&to&process&actions
&&&&if(GameFrame&==&0)&{
&&&&&&&&if(LockStepTurn())&{
&&&&&&&&&&&&GameFrame++;
&&&&}&else&{
&&&&&&&&//update&game
&&&&&&&&SceneManager.Manager.TwoDPhysics.Update&(GameFramesPerSecond);
&&&&&&&&List&finished&=&new&List();
&&&&&&&&foreach(IHasGameFrame&obj&in&SceneManager.Manager.GameFrameObjects)&{
&&&&&&&&&&&&obj.GameFrameTurn(GameFramesPerSecond);
&&&&&&&&&&&&if(obj.Finished)&{
&&&&&&&&&&&&&&&&finished.Add&(obj);
&&&&&&&&&&&&}
&&&&&&&&foreach(IHasGameFrame&obj&in&finished)&{
&&&&&&&&&&&&SceneManager.Manager.GameFrameObjects.Remove&(obj);
&&&&&&&&GameFrame++;
&&&&&&&&if(GameFrame&==&GameFramesPerLocksetpTurn)&{
&&&&&&&&&&&&GameFrame&=&0;
}IHasGameFrame接口有一个方法叫做GameFrameTurn,它以当前每秒游戏帧的个数为参数。一个具体的带游戏逻辑的对象应该基于GameFramesPerSecond来计算。比如说,如果一个单位正在攻击另一个单位,而且他攻击频率为每秒钟10点伤害,你可能会通过将它除以GameFramesPerSecond来添加伤害。而GameFramesPerSecond会根据性能进行调整。IHasGameFrame接口也有属性标记着结束。这使得实现IHasGameFrame的对象可以通知游戏帧循环自己已经结束。一个例子就是一个对象跟着路径行走,而在到达目的地之后,这个对象就不再需要了。帧同步回合为了与其他客户端保持同步,每次帧同步回合我们都要问以下问题:我们已经收到了所有客户端的下一轮动作了吗?每个客户端都确认得到我们的动作了吗?我们有两个对象,ConfirmedActions和PendingActions。这两个都有各自可能收到消息的集合。在我们进入下一个回合之前,我们会检查这两个对象。private&bool&NextTurn()&{&&&&&&&
&&&&if(confirmedActions.ReadyForNextTurn()&&&&pendingActions.ReadyForNextTurn())&{
&&&&&&&&//increment&the&turn&ID
&&&&&&&&LockStepTurnID++;
&&&&&&&&//move&the&confirmed&actions&to&next&turn
&&&&&&&&confirmedActions.NextTurn();
&&&&&&&&//move&the&pending&actions&to&this&turn
&&&&&&&&pendingActions.NextTurn();
&&&&&&&&return&
&&&&return&
}动作动作,也就是命令,都通过实现IAction接口来通信。有着一个无参数函数叫做ProcessAction()。这个类必须为Serializable。这意味着这个对象的所有字段也是Serializable的。当用户与UI交互,动作的实例就会创建,然后发送到我们的帧同步管理器的队列中。队列通常在游戏太慢而用户在一个帧同步回合中发送多于一个命令的时候用到。虽然每次只能发送一个命令,但没有一个会忽略。当发送动作到其他玩家的时候,动作实例会序列化为字节数组,然后被其他玩家反序列化。一个默认的”非动作”对象会在用户没有执行任何操作的时候发送。而其他则会根据特定游戏逻辑而定。这里是一个创建新单位的动作:using&S
using&UnityE
[Serializable]
public&class&CreateUnit&:&IAction
&&&&int&owningP
&&&&int&buildingID;
&&&&public&CreateUnit&(int&owningPlayer,&int&buildingID)&{
&&&&&&&&this.owningPlayer&=&owningP
&&&&&&&&this.buildingID&=&buildingID;
&&&&public&void&ProcessAction()&{
&&&&&&&&Building&b&=&SceneManager.Manager.GamePieceManager.GetBuilding(owningPlayer,&buildingID);
&&&&&&&&b.SpawnUnit();
}这个动作会依赖于SceneManager的静态引用。如果你不喜欢这个实现,可以修改IAction接口,使得ProcessAction接收一个SceneManager实例。实例代码可以在这里找到:第二部分概览在上次实现的帧同步模型当中,游戏帧率和通信频率(也就是帧同步长度)长度是固定间隔的。但实际上,每个玩家的延迟和性能都不同的。在update中会跟踪两个变量。第一个是玩家通信的时长。第二个则是游戏的性能时长。移动平均数为了处理延迟上的波动,我们想快速增加帧同步回合的时长,同时也想在低延迟的时候减少。如果游戏更新的节奏能够根据延迟的测量结果自动调节,而不是固定值的话,会使得游戏玩起来更加顺畅。我们可以累加所有的过去信息得到”移动平均数”,然后根据它作为调节的权重。每当一个新值大于平均数,我们会设置平均数为新值。这会得到快速增加延迟的行为。当值小于当前平均值,我们会通过权重处理该值,我们有以下公式:newAverage=currentAverage?(1–w)+newValue?(w)其中0<w<1在我的实现中,我设置w=0.1。而且还会跟踪每个玩家的平均数,而且总是使用所有玩家当中的最大值。这里是增加新值的方法:public&void&Add(int&newValue,&int&playerID)&{
&&&&if(newValue&>&playerAverages[playerID])&{
&&&&&&&&//rise&quickly
&&&&&&&&playerAverages[playerID]&=&newV
&&&&}&else&{
&&&&&&&&//slowly&fall&down
&&&&&&&&playerAverages[playerID]&=&(playerAverages[playerID]&*&(9)&+&newValue&*&(1))&/&10;
}为了保证计算结果的确定性,计算只使用整数。因此公式调整如下:newAverage=(currentAverage?(10–w)+newValue?(w))/10其中0<w<1而在我的例子中,w=1。运行时间平均数每次游戏帧更新的时间是由运行时间平均数决定的。如果游戏帧要变得更长,那么我们需要降低每次帧同步回合更新游戏帧的次数。另一方面,如果游戏帧执行得更快了,每次帧同步回合可以更新游戏帧的次数也多了。对于每次帧同步回合,最长的游戏帧会被添加到平均数中。每次帧同步回合的第一个游戏帧都包含了处理动作的时间。这里使用Stopwatch来计算流逝的时间。private&void&ProcessActions()&{
&&&&//process&action&should&be&considered&in&runtime&performance
&&&&gameTurnSW.Start&();
&&&&//finished&processing&actions&for&this&turn,&stop&the&stopwatch
&&&&gameTurnSW.Stop&();
private&void&GameFrameTurn()&{
&&&&//start&the&stop&watch&to&determine&game&frame&runtime&performance
&&&&gameTurnSW.Start();
&&&&//update&game
&&&&GameFrame++;
&&&&if(GameFrame&==&GameFramesPerLockstepTurn)&{
&&&&&&&&GameFrame&=&0;
&&&&//stop&the&stop&watch,&the&gameframe&turn&is&over
&&&&gameTurnSW.Stop&();
&&&&//update&only&if&it&#39;s&larger&-&we&will&use&the&game&frame&that&took&the&longest&in&this&lockstep&turn
&&&&long&runtime&=&Convert.ToInt32&((Time.deltaTime&*&1000))/*deltaTime&is&in&secounds,&convert&to&milliseconds*/&+&gameTurnSW.ElapsedM
&&&&if(runtime&>&currentGameFrameRuntime)&{
&&&&&&&&currentGameFrameRuntime&=&
&&&&//clear&for&the&next&frame
&&&&gameTurnSW.Reset();
}注意到我们也用到了Time.deltaTime。使用这个可能会在游戏以固定帧率执行的情况下与上一帧时间重叠。但是,我们需要用到它,这使得Unity为我们所做的渲染以及其他事情都是可测量的。这个重叠是可接受的,因为只是需要更大的缓冲区而已。网络平均数拿什么作为网络平均数在这里不太明确。我最终使用了Stopwatch计算从玩家发送数据包到玩家确认动作的时间。这个帧同步模型发送的动作会在未来两个回合中执行。为了结束帧同步回合,我们需要所有玩家都确认了这个动作。在这之后,我们可能会有两个动作等待对方确认。为了解决这个问题,用到了两个Stopwatch。一个用于当前动作,另一个用于上一个动作。这被封装在ConfirmActions类当中。当帧同步回合往下走,上一个动作的Stopwatch会成为这一个动作的Stopwatch,而旧的”当前动作Stopwatch”会被复用作为新的”上一个动作Stopwatch”。public&class&ConfirmedActions
&&&&public&void&NextTurn()&{
&&&&&&&&...
&&&&&&&&Stopwatch&swapSW&=&priorSW;
&&&&&&&&&&&&&
&&&&&&&&//last&turns&actions&is&now&this&turns&prior&actions
&&&&&&&&...
&&&&&&&&priorSW&=&currentSW;
&&&&&&&&//set&this&turns&confirmation&actions&to&the&empty&array
&&&&&&&&...
&&&&&&&&currentSW&=&swapSW;
&&&&&&&&currentSW.Reset&();
}每当有确认进来,我们会确认我们接收了所有的确认,如果接收到了,那么就暂停Stopwatch。public&void&ConfirmAction(int&confirmingPlayerID,&int&currentLockStepTurn,&int&confirmedActionLockStepTurn)&{
&&&&if(confirmedActionLockStepTurn&==&currentLockStepTurn)&{
&&&&&&&&//if&current&turn,&add&to&the&current&Turn&Confirmation
&&&&&&&&confirmedCurrent[confirmingPlayerID]&=&
&&&&&&&&confirmedCurrentCount++;
&&&&&&&&//if&we&recieved&the&last&confirmation,&stop&timer
&&&&&&&&//this&gives&us&the&length&of&the&longest&roundtrip&message
&&&&&&&&if(confirmedCurrentCount&==&lsm.numberOfPlayers)&{
&&&&&&&&&&&&currentSW.Stop&();
&&&&}&else&if(confirmedActionLockStepTurn&==&currentLockStepTurn&-1)&{
&&&&&&&&//if&confirmation&for&prior&turn,&add&to&the&prior&turn&confirmation
&&&&&&&&confirmedPrior[confirmingPlayerID]&=&
&&&&&&&&confirmedPriorCount++;
&&&&&&&&//if&we&recieved&the&last&confirmation,&stop&timer
&&&&&&&&//this&gives&us&the&length&of&the&longest&roundtrip&message
&&&&&&&&if(confirmedPriorCount&==&lsm.numberOfPlayers)&{
&&&&&&&&&&&&priorSW.Stop&();
&&&&}&else&{
&&&&&&&&//TODO:&Error&Handling
&&&&&&&&log.Debug&("WARNING!!!!&Unexpected&lockstepID&Confirmed&:&"&+&confirmedActionLockStepTurn&+&"&from&player:&"&+&confirmingPlayerID);
}发送平均数为了让一个客户端向其他客户端发送平均数,Action接口修改为一个有两个字段的抽象类。[Serializable]
public&abstract&class&Action
&&&&public&int&NetworkAverage&{&&&}
&&&&public&int&RuntimeAverage&{&&&}
&&&&public&virtual&void&ProcessAction()&{}
}每当处理动作,这些数字会加到运行平均数。然后帧同步回合以及游戏帧回合开始更新private&void&UpdateGameFrameRate()&{
&&&&//log.Debug&("Runtime&Average&is&"&+&runtimeAverage.GetMax&());
&&&&//log.Debug&("Network&Average&is&"&+&networkAverage.GetMax&());
&&&&LockstepTurnLength&=&(networkAverage.GetMax&()&*&2/*two&round&trips*/)&+&1/*minimum&of&1&ms*/;
&&&&GameFrameTurnLength&=&runtimeAverage.GetMax&();
&&&&//lockstep&turn&has&to&be&at&least&as&long&as&one&game&frame
&&&&if(GameFrameTurnLength&>&LockstepTurnLength)&{
&&&&&&&&LockstepTurnLength&=&GameFrameTurnL
&&&&GameFramesPerLockstepTurn&=&LockstepTurnLength&/&GameFrameTurnL
&&&&//if&gameframe&turn&length&does&not&evenly&divide&the&lockstep&turn,&there&is&extra&time&left&after&the&last
&&&&//game&frame.&Add&one&to&the&game&frame&turn&length&so&it&will&consume&it&and&recalculate&the&Lockstep&turn&length
&&&&if(LockstepTurnLength&%&GameFrameTurnLength&>&0)&{
&&&&&&&&GameFrameTurnLength++;
&&&&&&&&LockstepTurnLength&=&GameFramesPerLockstepTurn&*&GameFrameTurnL
&&&&LockstepsPerSecond&=&(1000&/&LockstepTurnLength);
&&&&if(LockstepsPerSecond&==&0)&{&LockstepsPerSecond&=&1;&}&//minimum&per&second
&&&&GameFramesPerSecond&=&LockstepsPerSecond&*&GameFramesPerLockstepT
&&&&PerformanceLog.LogGameFrameRate(LockStepTurnID,&networkAverage,&runtimeAverage,&GameFramesPerSecond,&LockstepsPerSecond,&GameFramesPerLockstepTurn);
}更新:支持单个玩家自从本文发出以来,增加了单人模式得支持。特别感谢的Dan提供。可以在以下看到修改:源代码:原文:
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量5508点击量3661点击量3334点击量3160点击量2859点击量2684点击量2626点击量2566点击量2514
&2016 Chukong Technologies,Inc.
京公网安备89}

我要回帖

更多关于 unity 切换相机 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信