求助,为什么我的vue props type游戏里不能放到建筑上

为什么我玩的游戏部落冲突,我所有的储备建筑加起来能有10500,但是为什么游戏里面显示最大只有7000呢?_百度知道
为什么我玩的游戏部落冲突,我所有的储备建筑加起来能有10500,但是为什么游戏里面显示最大只有7000呢?
我有更好的答案
生产资源的建筑里面的钱(比如说金矿里面的金币)不能直接使用,而要收取了以后才能用,简而言之你的总容量就是存钱罐X2+1000(大本营)
采纳率:48%
是两个储钱罐的总额么?
你别吧 金矿的算进去了
那金矿上显示的存储上限是什么?是不是也能存储,但是只是零时的,要等到存储罐使用后,才能收取?是吗?
嗯...金矿(包括圣水收集器) 有上限的
比如你每小时100
金矿(圣水收集器 后面不做说明)
上限是1000
那么10小时候他就会到达最大额度
你光看右上角的 圣水和金币上限就行了 不用算圣水收集器的
需要Q币解锁哦
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。503 Service Unavailable
No server is available to handle this request.分享到:33.6K
站内精确搜索
站内精确搜索 站内模糊搜索
Digital Tutors - Texturing Game Props in 3ds Max and Photoshop
Digital Tutors - Texturing Game Props in 3ds Max and PhotoshopDigital Tutors - 在3ds Max和Photoshop制作游戏模型的贴图 在这个过程中,我们将介绍在3ds Max和Photoshop中的贴图技术。当建立实时应用的低分辨率模型时,纹理发挥了很大的作用。很多时候我们无法增加面数时,可以用贴图来模拟的细节。此外,我们可以模拟光照效果或AO。在这个教程中,我们将创建和组装一个反坦克炮的贴图的过程。然后,我们将使用多种技术在Photoshop来绘制纹理贴图。最后,我们将烘焙出的环境光AO,我们可以整合我们的纹理。完成后,你就会有一个质感很强的枪。&In this course we will cover techniques for texturing &assets in 3ds Max and Photoshop. When building low resolution models &meant for real-time applications, texturing will play a big part in &communicating a lot of information about that model. Many of the details &that we are unable to add when modeling can be simulated with textures. &In addition, we can simulate lighting effects or occlusion in textures. &In this course, we'll go through the process of preparing our model, an &antitank gun, for texture by creating and assembling useable UV &layouts. We'll then use a variety of techniques in Photoshop to paint &texture maps for the gun. Finally, we'll bake out an ambient occlusion &map that we can integrate into our texture. Upon completion, you'll have &a textured gun, but you will also have the knowledge you need to start &creating textures for your own models.&
发布: | 查看:3038
| 发表时间: 15:39:40
原创文章如转载,请注明,转载至:
链接:http://pan.baidu.com/s/1mipzuy8 密码:z7bs
你不能用别人分享给你的如下方格式的地址再去分享来获得下载地址,文章的实际地址格式地址为:
地址里“&”以及后面的蓝色文字都要去掉,然后再使用以上其中一种分享方式 分享即可。
如果你还是不会下载,请加入Ghostxx 官方QQ群①:6095105,找站长和管理员帮助你解答!
全部评论 (0)
还可以输入
Ghostxx,CG网站,CG资源
本站是一个针对于CG、设计、艺术、建筑相关的公益性的网站收藏、资源分享网站。用户可以添加喜欢的CG网站、博客、微博 、空间、资源的地址,通过审核就可以放在页面上,用户也可以评价每个网站和资源,并且会影响网站和资源在本站的排行。
Copyright &2017 GhostXX all right reserved.
数据库查询次数:6
扫描二维码用移动设备打开本站[译者的话(新浪微博@滚石,欢迎大家一起讨论)]
由于公司目前没有专业的技术美术(Technical Artist),在最近的项目开发中碰了不少壁。于是我就特地做了一些关于美术方面优化的研究,听了一些讲座,阅读了一些文章,你即将阅读的这篇就是其中之一,我觉得不错,并在阅读中获得了共鸣,所以也就决定将这片文章翻译为中文。一来希望在公司中,对于我们的美术设计师作为一个参考,另外分享出来也希望,如果能有那么一点点帮助到跟我们遇到同样困扰的团队或者美术或者任何同仁们,我将万分荣幸。
由于我是一个程序猿,所以翻译中难免有些话或者美术专业术语译的比较二的地方,还请谅解。
原文地址:
———————————————————————-
你是否曾经开发了一个手机游戏,在你把它递交给reviewer时,本来满心希望它像百米冲刺一样高速运行,结果却步履蹒跚,甚至最后慢得跟爬行一样。
这很烂,不是么?我们意识到这本来是可以避免的,但很多小的错误和疏忽不断堆积,最终造成了程序的瘫痪:它太慢了!
介于我们一直这么粗心犯错,我们一定是忘了给自己的”角色属性表“加上预知未来的能力了 ?
如果一直这样,那我们开发的游戏——AlieNation,将变得根本不能玩。幸运的是,我们停止了错误,并开始搜集大家的想法,对游戏进行优化(优化成为了我们开发中的新王牌武器!)
为什么我们从2D转向3D
考虑到从测试玩家和本地媒体获得的认可,我们决定创建一个3D摧毁型游戏(destruction game),以填补市场上的空白。很惊讶大多数的摧毁型游戏都是2D的,但3D才是摧毁型游戏应该的样子,因为3D版本的感觉是如此的棒。当然,你需要做更多的工作来优化你的美术流水线(art pipeline)。
高效和优化让我在一些甚至不知晓的领域中感觉到从容温暖,所以在这里有一些事情要和你们分享,我铭记它们于脑中,并且一直提醒我的团队遵循它们,以保证我们的3D游能够持续运行流畅。
何时选择3D(何时保留2D)
作为一个3D游戏美术,我得到的第一个关键教训就是,让所有的东西尽可能的保持低模——不要使用额外的不必要的顶点,如果你确实要让你的模型拥有精度,把它放置在贴图中。比如说,我们的建筑物都是用简单的方块创建而成,然后贴上带有质感的细节纹理。
当我们要为Vogel教授(它只在我们游戏的GUI和HUD中出现)建模时,首先尝试的是3D模型,但是最终,我们还是把它分解为像Flash中那样的多层2D平面。它运行非常流畅,看上去也更棒,事实最终的结果比用3D模型赞非常多!使用带有动画的2D平面,可以很容易的创造细节,使得Vogel教授看起来非常酷,并吻合整个游戏的艺术风格。
现在每次我看到它时(下图的左下角就是Vogel教授),还会忍不住幸福热泪 ;-(
不要在贴图(纹理)优化上偷懒!
贴图大小是最容易被大多数美术设计师忽视优化的,要知道,贴图会占用非常非常多的空间!
通过跟几个设计师聊天,我发现他们中很多人都习惯于创建他们觉得舒适的尺寸,比如512×512,这符合常理,没什么错误,但唯一的问题就是,设计师不会在导出贴图之前,将它们降低到一个更加合理的尺寸。
一个更小更高效的贴图文件,在放置到游戏中后,可以和一个超大的贴图文件看起来效果一样好!但是如果设计师所有的工作都是在错误的贴图尺寸中进行的,很不幸,所有设计师花费了大量时间绘制的漂亮美术细节,在游戏中被降低尺寸后,会被无情的抹杀 ?
为了完全避免这个问题,我建议设计师应该在降低尺寸后对贴图做一些额外处理,亦或者是在创建时直接使用和在游戏中一样的分辨率。因为这样的话,设计师才能够精确掌控贴图中的每一个像素点。
文件格式,贴图重用和Shaders
PNG相比于其它很多格式没那么“邪恶”。相比于JPEG的有损压缩,它的压缩是无损的;它虽然对于Alpha透明的处理不像TGA那么出色,但是它既压缩文件同时又能保证足够好的Alpha映射,确实比其它的格式胜出一筹。
很多团队的教训是类似的:
不合理的贴图尺寸 + 不合理的文件格式 = 占用了太多的纹理空间 = 更慢的游戏 = 找到纹理并降低尺寸 = 丢失细节 = 浪费时间 = 非常沮丧的美术设计师 = 非常愤怒的美术总监 ?
另外一个关于贴图非常有用的建议是——尽可能的重用它们。很显然,这个不光对贴图,对其它任何游戏资源都应该如此。在我们创建Alienation的过程中,我们没有创建新的贴图用于几乎很难被看到的武器和金属物体上,而重用了建筑物的贴图用在这些非常小而琐碎的物体上。只需要一点UV技巧,你永远不会发觉它们的贴图其实是被重用的。
非常重要的一点,你要选择合适的Shader程序。当然,法线高光Shader(Bumped Specular Shader)用到物体上时,看起来太他妈的赞了,但是应该使用特定的手机定制shader,让游戏更高效。在Unity中,这比较容易做到,因为Unity内置了一整套专门针对手机的shader。
什么时候采用Alpha贴图,什么时候使用Mesh(网格模型)
Alpha贴图的卓越优点是它可以让一个物体的部分透明,非常容易,并且也相对的比较经济(cheaply)[作者注:这里说“经济”是相对的,是从处理消耗(processing cost)的角度说的。每一个像素,多边形,shader,light,粒子和骨骼,都会消耗你的游戏的处理性能。]
然而,在有些情况下,mesh比alpha贴图要更高效。举例说,当我们为汽车创建一个轮子时,为了表现轮子中间的缺口,建立轮子的网格模型要比使用贴图+透明的方式,来得高效的多。
我意识到,在这里我有点自相矛盾了,因为之前我说过,要把更多的细节放在贴图中,而不是网格模型上。记住一点,没有绝对的优化技术,如果你拥有这样的能力可以在特定时候选择使用最合理的优化技术,就造就了你不仅仅是一个游戏美术设计师,而且是一个伟大的牛逼的设计师 ?
粒子系统,draw calls,还有物理
对于任何游戏,视觉上的反馈(visual feedback)对于吸引玩家是异常重要的。如果对于玩家的操作缺少反馈,游戏会变得非常无聊,令人昏睡。
说到反馈:粒子效果是一个非常好的给玩家传递信息的方式。可能是一个子弹打到墙上产生的火花粒子效果,又或者是子弹射击一个纳粹分子的血溅粒子效果。
但是对于粒子,有些事情很容易犯错。第一点是对于粒子的贴图大小,对于贴图大小问题,本文的前半部分已经提到,如果你跳过了那部分,强烈建议你回到前面去看一看,我保证那部分内容能让你马上在贴图优化上变成一个更性感的人(而不是一个胡乱贴图的人):D
对于粒子系统的第二点是,很多人没有意识到,增加粒子个数,根本不会增加粒子效果的牛逼度——只会让游戏变得更慢。一个人可以用两种方式创建粒子效果:第一种是用尽可能少的发射数量,来达到一个最好的效果;另一种人就是搞2000个粒子,然后…慢慢跑吧…
你很少在现实游戏中见到过一个发射器包含2000个粒子效果,唯一的原因就是draw calls。
draw call是针对场景中每一个不同的物体而创建的(多一个draw call就会多增加一点游戏的处理时间)。对于粒子系统而言,在draw call上,很容易超载。
我喜欢制作一些粒子效果,但是每当我看到一些初级的设计师,看到一个漂亮而高效的粒子效果问:“只有10个粒子?太少了,这个如果用2000个粒子会更好”,我就巨他妈的沮丧,甚至肉体难受 ?
对于上图,厄….这…..你为什么要这么搞,非常差劲的初级美术。
没有什么比准确的射击到一个建筑物,并看到它优雅壮丽的倒下更酷了。
为什么会这样?所有的这一切都是因为甜美而性感的物理系统。
当说到物理系统时,程序猿可是这方面的专家。所以接下来的一段话,可能是一种程序猿专用的非常难以解析的语言,所以如果您是一位美术,在这里就忍忍吧 ?
在每一帧,物理系统都会更新很多次,物理会更新受其影响物体的位置,速度和其他一切跟运动相关的属性。每一帧物理的更新次数会直接影响到游戏的运行速度。所以加快游戏运行的一个方式就是减少每一帧物理系统被更新的次数。
这当然是一种可以尝试的方式,但是带来的坏处是会影响你的物理系统的准确性,所以平衡好每帧的更新次数,以获得最佳并最高效的物理效果是非常重要的。
一个例子,当我们减少了每帧物理更新的次数时,我们游戏中的一个球体可能穿过了一道墙,这…我觉得球穿墙可是不真实的,你呢?
所以要调节到一个最优的物理更新帧率是不容易的,这可花了我们公司的程序猿们好一段时间。
光照和天空盒
两件事情我会快速过下——光照和天空盒。
关于天空盒,很称奇的一点是其实你根本不需要使用它们作为天空!(我对吗?!?!)
一个非常棒的方式是在天空中包含背景艺术(background art),这使得能很经济简单的加入非常多的背景艺术和气氛到游戏中。
光照有点神秘,所以我直接了当,对于手机,最好使用平行光(directional light),或者如果你够幸运,加几盏点光(point light)也可以。在Alienation游戏中,在对light做了日以继日的研究后(我撒谎了,其实我就是google了一下-)),我发现聚光灯(spotlight)在手机游戏上是最消耗性能的光源,慎用。
结束这一切
本文中提到的所有建议,独立采纳其中的一条并不会让你的游戏马上得到改观。
但是如果你综合采纳所有的这些优化,你的游戏品质一定会马上得到质的提高,到时候,嗯哼,等着幸福的哭吧~
——————————————————————————
[译者的话]
个人觉得,这些建议非常好,并在游戏开发中时刻提醒自己遵循它们。但是因为大家开发的游戏都是各式各样的,使用的引擎也完全不同。不要把某种优化建议当成教条,应该选择最适合你们团队的。尽量多使用游戏引擎的profiling,以及时观察分析游戏中影响游戏系能的因素,而不要教条式的麻木follow各种优化建议。
Posted in: , , , ,
最近一个星期干了一件事情,因为游戏的需要,为Moai引擎集成了新浪微博的API,这样也算为开源又一次出了份力。
最近一年基本上是游走于游戏的脚本层,很久没有碰引擎代码了,这一周的工作充满了怀旧感,虽然复杂度不算高,但还是勾起了不少当年开发游戏引擎的记忆。
check it out from , branch: sina_weibo
同时该branch也集成了另一位Moai开发者的截屏API, from
如何使用[sample code]
MOAISim.openWindow ( &test&, 320, 480 )
viewport = MOAIViewport.new ()
viewport:setSize ( 320, 480 )
viewport:setScale ( 320, 480 )
layer = MOAILayer2D.new ()
layer:setViewport ( viewport )
MOAISim.pushRenderPass ( layer )
gfxQuad = MOAIGfxQuad2D.new ()
gfxQuad:setTexture ( &moai.png& )
gfxQuad:setRect ( -64, -64, 64, 64 )
prop = MOAIProp2D.new ()
prop:setDeck ( gfxQuad )
prop:setLoc ( 0, 80 )
layer:insertProp ( prop )
font = MOAIFont.new ()
font:loadFromTTF ( &arialbd.ttf&, & abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,.?!&, 12, 163 )
textbox = MOAITextBox.new ()
textbox:setFont ( font )
textbox:setRect ( -160, -80, 160, 80 )
textbox:setLoc ( 0, -100 )
textbox:setYFlip ( true )
textbox:setAlignment ( MOAITextBox.CENTER_JUSTIFY )
layer:insertProp ( textbox )
--MOAISinaWeiboIOS.init( &you app id&, &you app secret&, &your callback url& )
function openCompileDialog( txt, imgFileLoc )
MOAISinaWeiboIOS.compileDialog( txt, imgFileLoc )
MOAISinaWeiboIOS.setListener( MOAISinaWeiboIOS.DIALOG_POST_CANCEL_CLICKED, function()
print( &user clicked cancel post& )
MOAIFileSystem.deleteFile( imgFileLoc )
MOAISinaWeiboIOS.setListener( MOAISinaWeiboIOS.REQUEST_RESPONSE_WITH_RESULT, function()
print( &post successuflly& )
MOAIFileSystem.deleteFile( imgFileLoc )
function test()
local frameBounds={
width=1024,
height=768
local tmpFilename=MOAIScreenShotIOS.snapshotToFile( MOAIScreenShotIOS.PORTRAIT, frameBounds )
print( &Saved at location: &, tmpFilename );
print( MOAIFileSystem.checkFileExists( tmpFilename ))
if ( not MOAISinaWeiboIOS.isAuthValid() )
MOAISinaWeiboIOS.login()
MOAISinaWeiboIOS.setListener( MOAISinaWeiboIOS.DIALOG_LOG_IN_CANCEL, function()
MOAIFileSystem.deleteFile( tmpFilename )
MOAISinaWeiboIOS.setListener( MOAISinaWeiboIOS.SESSION_DID_LOGIN, function()
print( &successfully logged in& )
openCompileDialog( &test&, tmpFilename )
print( &already logged in, post& )
openCompileDialog( &test&, tmpFilename )
timer = MOAITimer.new()
timer:setSpan( 1 )
timer:setListener( MOAIAction.EVENT_STOP, test )
timer:start()
Posted in: , ,
Does Moai support an API like ‘setTimeout’?
Does Moai support an API like JavaScript’s setTimeout, which schedules a function execution after a specified time. I noticed there is a Moai class called MOAITimer, so I’m wondering if this class could be used for this purpose? I know a ‘setTimeout’ function could be easily realized on Lua level using MOAISim:getElapsedTime() and Lua’s ‘pcall’ function, but I’m just lazy and wonder if there is a shortcut that Moai has provided?
Thank you~
Moai doesn’t support that, but for convenience I’ll share the Lua function I use for this exact purpose:
(Hide some code)
EDIT: updated while investigating bug mentioned below. Will ensure this code works once bug is fixed.
Thank you for the sharing, but the function gives the following error after I run:
cannot use ‘…’ outside a vararg function near ‘…’
I think ‘…’ are also needed for “function()” in the 5th line, so I added it.
However, after I ran the following code
function printAStr( str )
print( str )
callWithDelay( 1, printAStr, &helloWorld& )
It prints “userdata: 0x21; instead of “helloWorld”, any clue for this? I think the core issue is how to pass arguments to the callback function of timer’s listener?
I made two mistakes in my function, which I have corrected above. You are correct I forgot to pass the … properly, and I also forgot that the timer listener callback receives the timer as the first parameter, which is why you were seeing the userdata print out in your test.
seems still unworkable for the arguments pass…
Ok, so my question is how to pass arguments to callback function of timer’s listener?
It looks like there may be a bug in the listener with the last release. I’ve opened a bug on the issue. I also realized yet another error in my above code, and have corrected it. Hopefully that’s the last edit I have to do. ?
Thanks for finding the bug. Won’t be able to get to it for a day or so. Sorry for the trouble!
OK, finally had time to look at this. It looks like it’s working correctly. The code I used is below.
MOAISim.openWindow ( &test&, 320, 480 )
function callWithDelay ( delay, func, ... )
local timer = MOAITimer.new ()
timer:setSpan ( delay )
timer:setListener ( MOAITimer.EVENT_TIMER_LOOP,
function ()
timer:stop ()
timer = nil
func ( unpack ( arg ))
timer:start ()
function printAStr ( str ) print( str ) end
callWithDelay ( 1, printAStr, &helloWorld& )
It works! Thanks a lot ?
Posted in: ,
黄金周第一天,将之前开发的Pacman移植到了iPad上。因为Moai引擎,移植变得很容易,只需要把input层做一些修改。我的第一个iOS上的game demo就此诞生 ?
已经将代码更新到了github –
Posted in: , ,
代码来自于最近写的Pacman,更多请查看 –
class.lua实现了在Lua中创建类的模拟,非常方便。class.lua参考自
-- class.lua
-- Compatible with Lua 5.1 (not 5.0).
function class(base, init)
local c = {}
-- a new class instance
if not init and type(base) == 'function' then
init = base
base = nil
elseif type(base) == 'table' then
-- our new class is a shallow copy of the base class!
for i,v in pairs(base) do
c._base = base
-- the class will be the metatable for all its objects,
-- and they will look up their methods in it.
c.__index = c
-- expose a constructor which can be called by &classname&(&args&)
local mt = {}
mt.__call = function(class_tbl, ...)
local obj = {}
setmetatable(obj,c)
-- below 2 lines are updated based on the Comments from 'http://lua-users.org/wiki/SimpleLuaClasses'
if init then
init(obj,...)
if class_tbl.init then
class_tbl.init(obj,...)
-- make sure that any stuff from the base class is initialized!
if base and base.init then
base.init(obj, ...)
return obj
c.init = init
c.is_a = function(self, klass)
local m = getmetatable(self)
while m do
if m == klass then return true end
m = m._base
return false
setmetatable(c, mt)
State基类,包含三个stub函数,enter()和exit()分别在进入和退出state时被执行,onUpdate()函数将会在state被激活时的每帧被执行。
require &class&
State = class()
function State:init( name )
self.name = name
function State:enter()
function State:onUpdate()
function State:exit()
StateMachine类,该类集成了的MOAIThread类。MOAIThread类似于Lua中的coroutine,但是在Moai中被yield的MOAIThread,会在game loop的每帧中被自动resume,见StateMachine:updateState函数,利用此特点,来实现每帧执行State:onUpdate函数。
require &State&
StateMachine = class()
function StateMachine:init()
self.currentState = nil
self.lastState = nil
function StateMachine:run()
if ( self.mainThread == nil )
self.mainThread = MOAIThread.new()
self.mainThread:run( self.updateState, self )
function StateMachine:stop()
if ( self.mainThread )
self.mainThread:stop()
function StateMachine:setCurrentState( state )
if ( state and state:is_a( State ) )
if ( state == self.currentState )
print( &WARNING @ StateMachine::setCurrentState - & ..
&var state [& .. state.name .. &] is the same as current state& )
self.lastState = self.currentState
self.currentState = state
if ( self.lastState )
print( &exiting state [& .. self.lastState.name .. &]& )
self.lastState:exit()
print( &entering state [& .. self.currentState.name .. &]& )
self.currentState:enter()
print( &ERROR @ StateMachine::setCurrentState - & ..
&var [state] is not a class type of State& )
function StateMachine:updateState()
while ( true )
if ( self.currentState ~= nil )
self.currentState:onUpdate()
coroutine.yield()
如何利用State和StateMachine类的示例,首先定义两个state。
SampleState.lua
require &State&
State1 = class( State )
function State1:init()
State.init( self, &State1& )
function State1:enter()
self.i = 0
function State1:exit()
self.i = 0
function State1:onUpdate()
print( self.name .. & is updated& )
self.i = self.i + 1
print( &self.i=& .. self.i )
if ( self.i == 10 )
print( state2 )
SM:setCurrentState( state2 )
self.i = 0
-----------------------
State2 = class( State )
function State2:init()
State.init( self, &State2& )
function State2:onUpdate()
print( &State2 is updated& )
require &StateMachine&
require &SampleState&
SM = StateMachine()
state1 = State1()
state2 = State2()
SM:setCurrentState( state1 )
Posted in: ,
贴上Pacman的类图,关于这个项目,见我的上一篇博客。
Posted in:
最近一两个星期,在业余时间利用Moai引擎重写了当年曾经用DirectX实现过的Pacman游戏。基于Lua的代码基本上是重写,只是保留使用了之前的2D资源。重写的目的一是通过这个来熟悉Moai引擎,二是希望用一个比较好的OO结构来重新实现Pacman,三则纯粹因为好玩。
把所有的代码,资源和文档都已经上传到了github,github真的是个好东东。
This is a prototype game based on the Moai game platform – . With various APIs for drawing and handling different 2D elements that Moai has supported, it clones a 2D Pacman game.
I programmed the game for several objectives. Firstly, because I am just new to Moai, I try to familiarize myself with Moai’s various APIs and game development based on it. I did encounter some issues when programming the game. Some were because I am a newbie for Moai, and some were testified later that were from Moai engine itself. Although the process was time-consuming, but to my pleasure, this is somewhat a contribution to the open source Moai engine itself. I feel a great joy to be part of it. Secondly, the whole game is based on OO. So I tried to program the whole game with a decent OO structure. Most importantly, some of the Lua classes or files, hopefully, can be reused in my other future potential games.
All in all, I programmed Moai-based Pacman for fun and share my code, related game assets and documents here. If there can be any help for you who are interested in Moai and OO game programming, that would be my best honer.
Posted in: , ,
(关于Moai,见我的上一篇文章。)
下面贴出在Moai Forum上我最近的两个问题帖。回答者都是Patrick,他是Moai引擎的领导者,同时也是’Zipline Games’的CTO。他似乎是个不错的人,在我每次贴出问题后,总是在最快的时间内解答。
1. Does MOAILayer2D only support 512 props maximumly?
I was trying to put lots of props onto a layer with function MOAILayer2D:insertProp, but found that if I inserted more than 512 props, all the props will fail to render on the layer. Is 512 the threshold for a layer’s prop count? Why is it a restriction? 512 doesn’t suffice my need. If there did exist a restriction, Moai even didn’t tell me any info or warning about the number 512, it took me lots of time to detect the problem and the number… ?
Sorry you had to spend your time on that. I agree that we could emit better warnings for situations like that.
The 512 restriction is arbitrary. It’s the size of the buffer created on the stack to sort the prims. I can fix it for you in the next release – will grow the buffer dynamically instead. When I have time to add an option to disable sorting by MOAILayer2D, the buffer won’t be needed at all.
If you need a fix immediately and are building from source, you can go into MOAILayer2D and just change it to something else.
(512 props is a lot! Is this for a mobile game? Mind if I ask what you’re displaying? Depending on what it is, may be other ways I can help.)
OK. I removed MAX_RENDERABLES and now use a flexible size buffer to gather and sort the props returned from the partition. That will be in the upcoming release. Try it out and let me know.
If you run into stuff like this in the future (any kind of odd or mysterious behavior) please let me know and I’ll hop right on it. Would much rather deal with a few false positives than have you waste your time!
Thanks so much for your reply and help, Patrick.
I’m just new to Moai. I was generating and displaying a map (31×28 grids) with each grid empty or filled with a type of sprite (like a the Pacman game, each grid could be filled with a Wall or a Bean or …). So this is why I used so MANY props. Do you have some other way or suggestions for generating such kind of game maps, so that not that many props will be used?
As you’ve fixed this issue, so I’m looking forward to the next release. Thanks a bunch!
Absolutely. Check out MOAITileDeck2D and MOAIGrid. Any of the deck types can be used in conjunction with a grid, but MOAITileDeck2D is ideally suited. The grid draws with one transform (from the prop it’s attached to) and also gets viewport culled on a per tile basis. It’s ideally suited to tiled backgrounds. You will get way better performance and can also update the grid values at runtime or animate them using a MOAIDeckRemapper. Check out tilemap-animated for a sample. In the games we are working on in-house we have multiple layers of tilemap parallax drawing hundreds and hundred of tiles at really good frame rates. If you need to animate a tile flipping or rotating (like what happens to a block in a Mario game when Mario hits it with his head), just hide the original tile in the grid and replace it with a prop – you can use the same tile deck as the source for the prop.
Also, can’t say enough good things about Tiled (mapeditor.org). The latest version supports flip flags and can output its data as Lua tables you can then load in to Moai (with a little bit of work).
Always a pleasure to help out, so let me know if you have more questions as you go.
Oh, how stupid I was that I didn’t find these classes, I will try these MOAI classes later. THANK YOU!
2. Questions about MOAIAction:stop
I have some questions regarding the usage of MOAIThread and MOAIAction
Firstly check out the code snippets below.
MOAISim.openWindow ( &test&, 320, 480 )&/span&
viewport = MOAIViewport.new ()
viewport:setSize ( 320, 480 )
viewport:setScale ( 320, 480 )
layer = MOAILayer2D.new ()
layer:setViewport ( viewport )
MOAISim.pushRenderPass ( layer )
gfxQuad = MOAIGfxQuad2D.new ()
gfxQuad:setTexture ( &cathead.png& )
gfxQuad:setRect ( -64, -64, 64, 64 )
prop = MOAIProp2D.new ()
prop:setDeck ( gfxQuad )
layer:insertProp ( prop )
function moveThreadMain()
movingAction = prop:moveLoc( 0, MOAIEaseType.LINEAR )
while ( movingAction:isBusy() )
x, y = prop:getLoc()
print( &current x - & .. x )
coroutine.yield()
isCrossBound = false
function checkThreadMain()
while ( true )
x, y = prop:getLoc()
if ( isCrossBound == false )
if ( x & 20 )
isCrossBound = true
movingAction:stop()
print( &x after stop current frame - & .. x )
print( &x after stop next frame - & .. x )
coroutine.yield()
moveThread = MOAIThread.new()
checkThread = MOAIThread.new()
moveThread:run( moveThreadMain )
checkThread:run( checkThreadMain )
I start and run 2 threads. The ‘moveThread’ uses MOAIAction to move the prop continuously and print its location each frame, while another ‘checkThread’ checks if the prop crosses some bound and if yes stops the moving animation with function MOAIAction:stop
But the result of MOAIAction:stop differs based on the launching sequence of those above 2 threads.
If I run ‘checkThread’ before ‘moveThread’, here is the result. MOAIAction::stop successfully stopped moving prop.
x after stop current frame – 20.
x after stop next frame – 20.
But if I run ‘movingThread’ before ‘checkThread’, I will get the following result, seems that animation will continue running for another frame
x after stop current frame – 20.
x after stop next frame – 20.
This really confuses me. Can anyone do me a favor to explain at which condition MOAIAction will run an additional frame after stopped, and when not? Is there a way to ensure that MOAIAction:stop will run no more frame no matter the running sequence of MOAIThread?
Just ran your code. It’s a legitimate bug in the way the MOAIAction update loop works. I’ve fixed it and checked it in to ‘master’ on moai-dev. We should have the next binary release out early next week.
Thanks for finding this!
Posted in:
, , , , , ,
最近通过澳洲朋友Andrew的介绍,开始使用了一款正在开发中的开源游戏引擎 – the mobile platform for pro game developers.
Moai是一款由’Zipline Games’公司开发的2D游戏引擎,我写这篇文章时的版本是0.5 Beta。它最大的特点就是集成了Lua脚本语言,它提供了一系列class-based的Lua API。开发者一般只需要通过写Lua脚本,即可实现一款游戏。而引擎本身解决了跨平台的问题,开发完的游戏可以顺利的发布到iOS和Android平台。而引擎本身提供的功能上,也基本上覆盖了需要开发一款2D游戏所有的元素,设备和输入,2D Sprite,动画,字体,粒子系统,物理,声音等。Moai的另外一个重要特色是提供了一个它称之为Moai Cloud的云服务,对于这个我还没有深研究,不过据称它可以让需要后端server的游戏变得简单,用户同样只需要用Lua来编写server的逻辑代码,而至于像scale-up这样的问题完全可以交给Moai引擎来处理。
然而开源引擎,尤其是处于Beta测试中的开源引擎,想用于开发正式的游戏确实有些不可靠。我最近在把之前写过的一个Pacman 2D移植到Moai上,但是遇到了很多的问题,并且一些问题最终通过层层纠结后证实是引擎本身的问题。虽然过程坎坷,但因为参与开源引擎项目,并有了自己的contribution,这本身着实是一件令人欣慰而振奋的事情。
但是希望Moai的开发者们work harder,让Moai尽快变得更好 ?
注册并获取Moai:
Moai入门文章:
第一部分:
第二部分:
Moai API Documentation:
Posted in:
, , , , , , , ,
写这篇博客的目的是为了对game loop(游戏主循环)做一个全面的总结和介绍,包括它的定义,与之相关的专业术语(terminology),以及最重要的,对它的几种实现方式,从代码层次做一些介绍以及优缺点分析。
1.什么是Game Loop
对任何一个游戏开发者来说,game loop都应该不是一个陌生的概念。任何一款游戏都会有一个自己的game loop,它是整个游戏的核心,是游戏的心脏。具体什么是game loop,这是来自于Game Engine Architecture[2]上对于game loop的定义:game loop是对于一个游戏(或者一个游戏引擎)的所有子系统(subsystem)的周期性的更新。一个游戏会包含各种不同的子系统(比如渲染,物理,动画,AI等等),这些不同的子系统负责处理不同的游戏任务,game loop的实现方式就决定了这些子系统的任务执行的组织方式。而这些就决定了整个游戏的基础架构,同时也将决定game在不同的机器上将如何运行,在下面介绍game loop的不同实现方式时会对此有详细介绍。
game loop中所处理的逻辑和操作包罗万象,但一般可抽象为以下这几种模块:
处理输入:这些输入包括,来自于交互设备(键盘,鼠标,游戏控制器等等)的输入,来自于网络的输入;
系统更新(update):(根据输入或者自发的)对各个子系统进行更新,以决定当前的游戏状态;
渲染(render):对整个游戏场景进行渲染。
所以一个最最简单的game loop可以抽象为如下的这些伪代码:
while ( game is running )
processInput();
2.相关术语
常常和渲染紧密相连的一个参数是FPS(frame per second),它指的是每秒显示设备在屏幕上进行绘制的频率。对于游戏来说,一般50-60的FPS是最优,最低也不要低于16帧[1]。游戏中还有一个容易被忽略的重要参数可以被称为游戏速度(game speed)[3],它指的是游戏状态每秒被更新的次数,容易与FPS混淆。一个通俗的理解和区别它们的方式可以是,FPS是render函数被调用的频率,而game speed则是update函数被调用的频率。
游戏是对实时性(real-time)要求很高的系统,所以在game loop中,time是尤其重要的一个因素。这里需要说的是,游戏中会包含好几种不同的时间,这些时间有着各自的时间线(timeline),并且在游戏中发挥着各自不同的重要作用[2]。
系统的真实时间(real time)。真实时间一般在操作系统被定义为从过去某个固定时间点(比如常见的一个时间点被称之为Epoch, 是日的0:00:00, UTC时间)到当前点总共逝去的时间。
游戏时间(game time)。一般情况下,游戏时间等于系统时间。但在某些特殊情况下,游戏时间可以发挥不同的作用:比如在游戏暂停的时候,需要停止更新游戏时间;在游戏需要以慢速运行时,可以用比真实时间低的频率来更新游戏时间,这在我们需要进行一些游戏调试的时候,尤其有用。下面会介绍到,一个好的game loop结构,可以有助于游戏调试,这对开发人员来说是非常重要的。
局部时间线(local timeline)。不管是一个动画,还是一个音频或者视频片段,都会有一个局部时间线。通过将此局部时间线以不同的方式映射到全局的时间线,可以灵活的控制片段以特定的方式进行播放。[2]
帧时间(frame time)。两次帧循环之间所间隔的时间,具体可看本文章的3.2.1节。
3.Game Loop的实现方式
OK,介绍了这么多跟game loop相关的概念,接下来进入正题,game loop的各种实现方式大盘点。
有一点需要强调的是,game loop在单线程的游戏系统中是一种情况,而在多线程的系统中又是另一个不同的故事。多线程对于系统会引入异常的复杂性,关于这个,可以看我之前的一篇关于游戏引擎多线程的文章:。相比来讲,单线程要简单很多,这篇文章里以下的一些game loop的实现方式也主要是针对在单线程环境中的。
在单线程环境中,game loop按照实现方式可以分为以下这几种类型:
基于帧的(frame-based)game loop
基于时间的(time-based)game loop
可变频率(variable-step)game loop
固定频率(fixed-step)game loop
3.1 基于帧的(frame-based)game loop
这是一种最简单的game loop,就如上面文章刚开始的伪代码所描述的一样,在这种结构下,游戏只是重复的不间隔的进行processInput,update,render操作。可以看到,这种game loop的实现异常简单,但缺点也是异常明显的。因为它缺少了一个很重要的控制因素——时间,所以在不同配置的机器环境下,游戏将以不同的速度运行。比如说,游戏中的一个物体,在每次的update操作中将它的位移增加5(单位可以是米,英尺,像素,或者其它某种单位,每个游戏都使用一个特定的距离单位):
void update()
object.position += 5.0f;
在一个较快速的机器上,update被执行的次数会更多,所以物体移动的速度就会更快,这就导致了游戏在不同机器运行速度的不一致,这是不能接受的。
优点:简单;
缺点:在不同机器上,游戏的运行速度不一致。
3.2 基于时间的(time-based)game loop
为了让不同机器的游戏运行速度一致,就需要引入时间,这就带来了基于时间的game loop实现方式。这种方式下,又可以分为两种,可变频率(variable step)的和固定频率的(fixed step)。在[5]中,这两种方式则分别被称为可变间隔的(variable interval)和固定间隔的(fixed interval)。
3.2.1 可变频率的game loop
这种方式的实现只需要为update函数引入一个时间参数elapsedTime即可,elapsedTime指的是从上一次loop的执行到当前loop所过去的时间,通常称之为帧时间(frame time),或者time delta。它的单位一般使用毫秒,这是在游戏中进行时间相关操作所使用的标准单位[2],当然其它比如在做profiling时为了计算某个函数的执行时间,则用的是机器周期(machine cycle)这样更精确的时间机制。
这是这种game loop实现的伪代码,也非常简单:
lastFrameTime = getCurrentTime();
while ( game is running )
processInput();
currentFrameTime = getCurrentTime();
elapsedTime = currentFrameTime - lastFrameT
update( elapsedTime );
lastFrameTime = currentFrameT
这时,再看前面的那个例子,对游戏中的一个物体,在每次的update操作中不是将它的位移绝对增加5,而是要乘以时间这个因子,这样5实际代表的是物体的速度。速度乘以时间,就得到了物体在这段时间内的位移:
void update( float elapsedTime )
object.position += 5.0f * elapsedT
这样就解决了在不同配置的机器上运行速度不一致的问题。因为在更快的机器上,update执行的频率高,但frame time的间隔时间较短,所以物体位置更新的频率高,但每次更新的位移幅度小。反之,在较慢的机器上,update函数的执行频率较低,但frame time间隔时间较长,则每次物体将以较大幅度的更新位移。正是因为引入了时间因素,所以在不同配置的机器上,游戏的运行速度将会看起来一致。
不过这种game loop在实现时需要解决一个长时间暂停的问题,所以我们在暂停时,要同时停止更新游戏时间,以免在暂停后恢复时,将得到一个超级大的elapsedTime值。
但随之而来,这种game loop实现方式的缺点也暴露出来了。比如考虑这样一种场景,物体绕着一个弧形的轨迹进行移动。在正常的速率下,物体的运行轨迹几乎是弧形的。如下图,图片来自于[5]。
但是在较慢的机器上,虽然物体的移动位置点能保持同步,因为更新的频率较低,物体的移动轨迹就变得非常离散,以至于不是按照一个弧形在移动,如下图。
其它的效果也有类似的问题,比如说动画,虽然说动画的播放速率是一致的,但是在较慢的机器上,会出现比较严重的掉帧现象,这就是我们俗称的“卡了”。再比如说物理,在一个正常的机器上,一个障碍物能够完美的被避开,但在一个较慢的机器上,这就不好说了。
对于这种game loop,就算是较快的机器上,也是有问题的,虽然好像update被执行的越快越多,游戏运行的就越流畅,用户的体验应该越好才对。但其实不然,两点原因,首先就算在较快的机器上,也可能会遭遇到运算的高峰期,这时由于对比明显,游戏性能的下降会非常明显,游戏用户就很容易察觉到这种性能降级(performance degradation),这并不是好的体验;再一点,对于手机等移动设备上的游戏,update速率执行过快不是好事,这对电池是一种消耗。事实是,游戏只需要一定范围内的update频率就可以达到流畅而令用户接受的效果。
优点:简单且不同机器上的游戏运行速度是一致的;
缺点:在较慢的机器上,物体的更新频率慢会导致各种效果失真(distortion);在较快的机器上,更新太快的话会更容易让用户察觉到性能降级,且对于移动设备,更新太快会降低电池的使用时间。
3.2.2 固定频率的game loop
由此,为了解决上面的问题,就有了这种固定频率的game loop,让游戏的更新速度保持在一个特定的恒定值。比如下面的这段伪代码,让游戏恒定运行在FPS(或者game speed,此时FPS等于game speed)为25的速度下。代码参考自[3]。
#define FRAME_RATE 25
#define FRAME_TIME ( 1000 / FRAME_RATE )
nextFrameTime = getCurrentTime();
while ( game is running )
processInput();
update( FRAME_TIME );
nextFrameTime +=
FRAME_TIME;
currentFrameTime = getCurrentTime();
if ( nextFrameTime &= currentFrameTime )
sleep( nextFrameTime - currentFrameTime );
// 我们已经跟不上帧速率了
可以看到,如果一次loop执行完的持续时间小于固定帧时间,则直接让主线程sleep即可。但是如果在较慢的机器上(或者是设定的固定帧速率过高),执行完一帧的时间会超过固定帧时间,导致无法达到所目标的帧速率,则只能忍受这种情况了。最差情况下,如果在某一时期内游戏遭遇到了非常巨大的运算压力,则游戏将会变得异常缓慢到无法忍受的地步。
保持固定的更新频率有一个非常重要的优点,因为它带来了游戏执行的确定性(game execution determinism)[1],所以以这种机制所实行的game loop可以被称为确定性的game loop(deterministic game loop)。反之,可变频率的game loop则是非确定性的(non-deterministic),因为它依赖于系统每一帧运行的时间,这在游戏每次运行时是变化不定的,这就导致游戏的行为也是不定的。
确定性机制能够为系统带来一个非常重要的特点——录制和回放功能(record and replay)[2]。所谓录制回放功能,就是能够将玩家在进行游戏的时候的各种操作记录下来,以便在下一次运行时,就能够通过回放将游戏以同样的方式执行。这会成为一个很好的debugging工具,因为通过回放功能,会让一些难以发现的bug得以轻而易举的复现,这是非常珍贵的。甚至我们还可以支持单步调试(single-stepping)功能[2],单步调试指的是当游戏处于暂停状态时,可以通过某个按键,让游戏一次执行一个frame time,这在调试游戏时都是非常有用的。
这种game loop还是有一个问题,它紧耦合了update和render的执行频率,update的频率(即game speed)保持在25基本能满足游戏运行的流畅需求,但让render的更新频率(即FPS)也保持在25,对于配置好的机器实在是有些浪费,我们可以让渲染的更快,以获得更好的画面效果。
优点:在不同机器上游戏效果一致,同时为游戏带来确定性。
缺点:一个更好的机器并不能带来更好的游戏画面,扩展性(scalability)差。
3.2.3 获得最大FPS的固定频率的game loop
解决上一个game loop缺点的办法就是解耦update和render,让它们以各自不同的频率运行。这就带来了这个固定频率的game loop的变种,称之为“获得最大FPS的固定频率的game loop”(Constant Game Speed with Maximum FPS)[3]。
同时这种方法还处理另一个问题,当update处理时间过久时,这种game loop会暂时不进行render而再次update。换言之,当update执行时间长于所期望的帧时间时,游戏会丢弃绘制帧并调用额外多次update函数,以让游戏从一个慢速(slowdown)状态中追赶上并恢复过来[4]。
这是该game loop的代码:
#define MAXIMUM_FRAME_RATE 45;
#define MINIMUM_FRAME_RATE = 15;
#define UPDATE_INTERVAL ( 1000 / MAXIMUM_FRAME_RATE )
#define MAX_CYCLES_PER_FRAME ( MAXIMUM_FRAME_RATE / MINIMUM_FRAME_RATE )
nextFrameTime = getCurrentTime();
while ( game is running )
loops = 0;
while( getCurrentTime() & nextFrameTime
&& loops & MAX_CYCLES_PER_FRAME )
update( UPDATE_INTERVAL );
nextFrameTime += UPDATE_INTERVAL;
从上面的代码中看出,当出现update处理时间过久时,game loop并不是一直重复执行update而不渲染,update频率被控制在15-45之间,所以在一次while循环中,最多只会执行3次update(MAX_CYCLES_PER_FRAME=MAXIMUM_FRAME_RATE / MINIMUM_FRAME_RATE=45/15)。
当然FPS也不总是可以为任意值,有时为了解决显示设备上的一种叫做tearing[2](屏幕的上半部分显示的是上一帧的画面,而下半部分是当前帧的画面)的问题,需要将FPS设置为显示设备刷新频率的倍数。在手机等这种移动设备上,同样为了省电,也需要将FPS固定在一个恒定值。但不管怎样,update和render仍然是以各自不同的频率运行。最近在读的一本关于iOS游戏开发的书上[6]的game loop,作者使用的就是这种game loop,它的game speed被设置在15-45,而FPS则采用的是iOS上默认的60(iOS上使用CADisplayLink的frameInterval属性来设置绘制帧率,frameInterval默认是1,表示显示1秒钟会被刷新60次)。
优点:拥有固定频率的game loop的优点,同时解耦update和render,并且当帧速率降低时,可以通过丢弃绘制帧来保持游戏的速度;
缺点:在高配置的机器上依然有些浪费资源。
3.3 其它方式
game loop还可以有更多的变化,比如在[1]中提到,对于游戏中的不同子系统,update的频率也是不一致的。比如,为了获得很好的动画效果,需要一个较高的频率来更新动画,而对于AI系统,用同等高频率的速度来更新就是浪费计算资源了。所以这篇文章提出了一种更好的update机制,将update分隔为两部分,一部分以最快的速度运行,而另一部分以某种预设的固定频率运行。
在[3]中提出,为了获得一种更平滑的画面效果,可以对frame-time进行插值(interpolate),并且为update函数提供预测函数(prediction function)。
一个看似简单的game loop,也可以有这么多的变数。这篇文章主要基于时间因素列出了4种实现方式:第1种是基于帧的game loop,一般而言要避免采用这种方式,而应该选用后面3种基于时间的game loop。基于时间的可变频率的game loop是一种常见的实现方式,不过为了获得稳定的画面效果和游戏运行的确定性,可以使用固定频率的game loop。最后还可以将update和render解耦以各自频率运行,以获得最优的组织结构运行游戏。
OK,这里就是我有史以来最长的一篇博客的结尾。
[1]LUIS VALENTE, Real Time Game Loop Models for Single-Player Computer Games
[2]Jason Gregory, Game Engine Architecture
[6]Michael Daley, Learning iOS Game Programming - A Hands-On Guide to Building Your First iPhone Game
Posted in:
Infinity Theme by}

我要回帖

更多关于 matlab regionprops 的文章

更多推荐

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

点击添加站长微信