Where there is a dream ,there is hope

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  64 Posts :: 0 Stories :: 8 Comments :: 0 Trackbacks

常用链接

留言簿(1)

我参与的团队

搜索

  •  

最新评论

阅读排行榜

评论排行榜

#

晚上 抽风 ,折腾电脑 ,安 了 个 UBUNTU.
夜深了 ,明早起 来 在 这 下 面 写个 小 程序玩下 .

posted @ 2010-10-23 01:26 IT菜鸟 阅读(183) | 评论 (0)编辑 收藏

综述

参看前面的教程基础, 对象创建, 时钟 , 框架层次结构动画 视口与摄像机,和 声音与音乐
这篇教程介绍了什么是特效以及如何创建它们
特效是将曲线及其组合而成的一组数据(正弦线、三角型边、矩形或者线性),应用在不同类型的参数中。如:缩放、旋转、位置、速度、颜色等。

特效在配置文件中设置,仅仅只需要一行代码就可以在对象上使用这些特效。
可以有最多8条任意类型的曲线组合在一起形成一个特效。
在同一时间,可以有最多4个特效应用于同一个对象上面。

特效可以使用绝对值或者相对值,这取决于配置文件中Absolute标签。
控制曲线的周期、相位、和振幅都是允许的。
对于位置和速度特效来说,输出值可以使用对象的方向 和/或 缩放值,以相对方式应用于对象目前的状态。

这也就允许我们创造极其拉风的视觉特效。

除非特效已经缓存在内存中,否则特效参数全部在配置文件中进行调整,并且使用退格键来即时重载。 (cf.通过 KeepInCache 属性来实现内存的缓存).
比如说:你不能调整正在运行的循环特效,因为他已经在默认的配置文件中定义好了。在这个测试程序运行的时候,所有其它的特效能够被更新。

通常说来,随机值的使用可以给特效带来更多的变化。
比如, 晃动方式的缩放(the wobble scale), 带颜色的闪光(the flash color) 和 攻击式的移动(the “attack” move) 等特效就使用了少量的随机值.

就像显示事件一样,我们也可以注册特效的开始播放和停止的事件。因为循环时间是永远不会停下来的,所以对应的停止事件(orxFX_EVENT_STOP)永远不会发生. 我们也会简单的介绍一下如何一些个性数据(仅仅包含一个布尔值的结构)添加到orxOBJECT中。1)
在事件的回调函数中,我们通过它,在特效开始的时候为对象加锁,在结束的时候解锁。
我们使用锁是为了让soldier(士兵)在同一时刻只有一个特效在发挥作用。
把这些东西写在这里,仅仅具有教育意义。2)

详细内容

通常,我们先载入配置文件,创建一个时钟,然后注册更新函数,最后,创建我们的士兵和盒子对象。请在之间的教程中获取更多信息。 .

然后,我们注册输入和特效事件

orxEvent_AddHandler(orxEVENT_TYPE_FX, EventHandler);
orxEvent_AddHandler(orxEVENT_TYPE_INPUT, EventHandler);

大家可以看到,在这两个事件中,我们使用了同一个回调函数(EventHandler).

现在我们迅速的扫一眼自己的“对象”数据结构。

typedef struct MyObject
{
orxBOOL bLock;
} MyObject;

接下来,看看如何用 orxObject_SetUserData()将它绑定到soldier上

MyObject *pstMyObject;
 
pstMyObject = orxMemory_Allocate(sizeof(MyObject), orxMEMORY_TYPE_MAIN);
pstMyObject->bLock = orxFALSE;
 
orxObject_SetUserData(pstSoldier, pstMyObject);

现在看看如何在Update函数中使用特效

orxSTRING zSelectedFX;
 
if(orxInput_IsActive("SelectWobble"))
{
zSelectedFX = "WobbleFX";
}
else if(orxInput_IsActive("SelectCircle"))
{
zSelectedFX = "CircleFX";
}
 
[...]
 
// Soldier not locked?
if(!((MyObject *)orxObject_GetUserData(pstSoldier))->bLock)
{
if(orxInput_IsActive("ApplyFX") && orxInput_HasNewStatus("ApplyFX"))
{
orxObject_AddFX(pstSoldier, zSelectedFX);
}
}

可以看到,我们通过orxObject_GetUserData()这个函数得到了我们想要的数据,向solder里添加特效的方法跟添加声音的方法如出一辙,用的都是这个函数orxObject_AddFX()。

接下来,看看EventHandler这个函数

首先是输入方面,这里只展示了每次输入时哪个按键被使用了。

if(_pstEvent->eType == orxEVENT_TYPE_INPUT)
{
if(_pstEvent->eID == orxINPUT_EVENT_ON)
{
orxINPUT_EVENT_PAYLOAD *pstPayload;
 
pstPayload = (orxINPUT_EVENT_PAYLOAD *)_pstEvent->pstPayload;
 
if(pstPayload->aeType[1] != orxINPUT_TYPE_NONE)
{
orxLOG("[%s] triggered by '%s' + '%s'.", pstPayload->zInputName, orxInput_GetBindingName(pstPayload->aeType[0], pstPayload->aeID[0]), orxInput_GetBindingName(pstPayload->aeType[1], pstPayload->aeID[1]));
}
else
{
orxLOG("[%s] triggered by '%s'.", pstPayload->zInputName, orxInput_GetBindingName(pstPayload->aeType[0], pstPayload->aeID[0]));
}
}
}

正如你所见,我们通过按下的是一个单键还是一个组合键来判断展示不同的信息。

我们仅使用了两个首次输入点,因为我们知道,我们的配置文件中没有超过两个的组合键。尽管orx支持最多四个组合键来做为一个单键。

orxInput_GetBindingName() 函数给了我们一个输入的文字显示。

注意:这些名称在配置文件中也绑定到了对应的按键上面。

现在来看下如何处理这个事件

if(_pstEvent->eType == orxEVENT_TYPE_FX)
{
orxFX_EVENT_PAYLOAD *pstPayload;
orxOBJECT           *pstObject;
 
pstPayload = _pstEvent->pstPayload;
pstObject  = orxOBJECT(_pstEvent->hRecipient);
 
switch(_pstEvent->eID)
{
case orxFX_EVENT_START:
orxLOG("FX <%s>@<%s> has started!", pstPayload->zFXName, orxObject_GetName(pstObject));
 
if(pstObject == pstSoldier)
{
// Locks it
((MyObject *)orxObject_GetUserData(pstObject))->bLock = orxTRUE;
}
break;
 
case orxSOUND_EVENT_STOP:
orxLOG("FX <%s>@<%s> has stoped!", pstPayload->zFXName, orxObject_GetName(pstObject));
 
if(pstObject == pstSoldier)
{
// Unlocks it
((MyObject *)orxObject_GetUserData(pstObject))->bLock = orxFALSE;
}
break;
}
}

在soldier上的动画开始的时候,我们用自己的数据结构来锁定它,相应的,停止的时候解锁。

看完了代码部分,我们再去看看配置文件。

首先看个简单的特效 :盒子上旋转的特效。

[RotateLoopFX]
SlotList  = Rotate
Loop      = true
 
[Rotate]
Type        = rotation
StartTime   = 0.0
EndTime     = 2.0
Curve       = sine
Pow         = 2.0
StartValue  = 0
EndValue    = 360
 
[Box]
FXList = RotateLoopFX

看到了吧,特效是在它创建之初直接应用在盒对象上面的,而不是在代码中。

RotateLoopFX包含仅包含一个时间段(Rotate)并且一直循环(attribute Loop)

然后定义Rotates时间段。时间的单位都是秒,角度的单位都是度。

定义这个旋转动画的时候,我们使用了一个正弦曲线,让他每两秒旋转360度。

下面看下我们的摇摆特效。

[WobbleFX]
SlotList = Wobble
 
[Wobble]
Type          = scale
StartTime     = 0.0
EndTime       = 1.0
Period        = 0.2
Curve         = sine
Amplification = 0.0
StartValue    = (1.0, 1.0, 1.0)
EndValue      = (2.0, 2.0, 1.0) ~ (6.0, 6.0, 1.0)

我们修改了scale属性,并赋予它一个StartValue(开始值)和EndValue(结束值)。
他们都是用向量来表示的,如果不想使用任何各向异性的值(译者注:专业名词anisotropic(各向异性)去知道确切意思)的话,也可是使用float类型来表示。
虽然看起来我们正在使用一个isotropic(各向同性)3)的值,这个EndValue也不过是一个随机值。

也就是说,它的X和Y部分可能是完全统统的随机值!

除此之外,我们使用了一个简单的周期为0.2 秒的正弦曲线,它将会播放1秒钟。

看到了吧,我们将Amplification(增幅) 的值设为0,这就是说,随着时间的推进,曲线的振幅会逐渐变低。注意:默认的Amplification是1,表示不随时间变化,保持稳定,当值大于1时,振幅就会加大;当值小于1时,振幅就会减少。

看看圆是如何运动的。

[CircleFX]
SlotList        = CircleX#CircleY
KeepInCache     = true
 
[CircleX]
Type            = position
StartTime       = 0.0
EndTime         = 1.0
Curve           = sine
StartValue      = (0.0, 0.0, 0.0)
EndValue        = (-50.0, 0.0, 0.0)
UseOrientation  = true
UseScale        = true
 
[CircleY@CircleX]
Phase       = 0.25
StartValue  = (0.0, -25.0, 0.0)
EndValue    = (0.0, 25.0, 0.0)

Here we need to use 2 slots that affects the position so as to be able to have a circle motion.
The first slot, CircleX, will apply a sine curve on the X component of our object's position.
The second slot, CircleY, will apply the same curve (with a different amplitude) on its Y component.

我们使用两个时间段来控制它的位置,这样才能做出一个圆形的运动。第一个时间段是CircleX,他将会应用在对象的X轴向的振幅。第二个时间段CircleY,会产生一个同样幅度的作用效果在Y轴上。

如果我们不更改CircleY的相位,是不会发生圆形的运动。

现在假设一个正弦曲线,在初始值(StartValue)是相位0,准备增加

在相位0。25的时候,到达中间点,将会继续增加

在相位0.5的时候,到达最高值(EndValue),准备下降

在相位0.75的时候,回到中间点,继续下降

在相位1.0的时候,就跟相位0(StartValue)是一样的了

注意:这段描述正弦曲线的工作过程也同样适用于三角形,但是却不适用于线形。

我们将略过大多数其他的特效,因为那里没有什么我们不知道的新知识了。

但是我们还是要迅速的看一眼翻转的特效,他将会向我们展示如何翻转一个对象。就像Paper Mario Wii4)的风格.

[FlipFX]
SlotList = Flip
 
[Flip@Wobble]
EndTime       = 0.5
Period        = 1.0
Amplification = 1.0
EndValue      = (-1.0, 1.0, 1.0)

看到了吧,我们很简单的使用负值完成了这个效果! =)
同时也注意到,我们给Period(周期)设了一个明确的值。
我们选了一个两倍于定义的正弦曲线的Period,这样我们就只使用了正弦曲线的上升的那一半。同时,我们也将Amplification改回了1。(在”“Wobble”“中被设为0)

资源

源代码: 07_FX.c

配置文件: 07_FX.ini

1) 九天注:这里作者有点穿越了,需要看下面的例子才能懂,作者定义了一个仅包含一个Bool值的结构MyObject,这里的括号,作者打在了orxOBJECT后面,我费解了N久,其实应该修饰个性数据,所以个人调整了一下。
2) 九天注:本来一个对象可以同时有4个特效发生,这里作者仅仅是告诉你怎么使用“个性数据”才这样做的,所以说仅仅具有教育意义。
3) Z值不影响2D元素
4) 九天注:Wii上的纸片马里奥是个很出名的游戏,作者的意思就是这里的flip描述的就是那个游戏里面的风格和效果
posted @ 2010-07-07 08:58 IT菜鸟 阅读(288) | 评论 (0)编辑 收藏

     摘要: 综述 这篇教程介绍了什么是特效以及如何创建它们   特效是建立在曲线集(线、三角形、矩形、)之上的,它们具有不同参数如:大小、旋度、位置、速度、颜色等。   特效在配置文件中设置,仅仅只需要一行代码就可以在对象上使用这些特效。 8条任意类型的曲线组合在一起就可以形成一个特效。 在同一时间,可以有多达4个特效应用于同一个对象上面。   特效可以使用绝对...  阅读全文
posted @ 2010-07-06 13:27 IT菜鸟 阅读(388) | 评论 (0)编辑 收藏

原文:http://orx-project.org/wiki/en/orx/tutorials/anim

综述

这篇教程只涉及了orx中最基本的动画使用。

更多关于动画的东西在这里 猛击我.

图定义了动画间所有可能的切换方式。动画通过一个唯一的字符串来引用。所有的切换和动画都是通过配置文件来创建的。
当一个动画被请求的时候,引擎会计算从当前动画到请求动画之间的链路
如果这个链路存在,它会自动执行。用户将通过事件被告知动画何时开始、停止、删节或者循环。
如果我们不能具体制定任何目标动画,引擎就会很自然的沿着属性中定义的线路(走下去)。
也有一个方法来越过这个寻路过程并且迅速的指向一个动画。

详细内容

通常,我们先载入config file(配置文件),创建一个viewport,创建一个clock(时钟)并且注册Update(更新)函数,最后创建一个主对象。
请从之前的教程中获得更多的信息。

现在我们开始从代码入手,我们将会从本页的底部看到数据是如何组织的。
在Update函数中,当输入GoLeft激活的时候会触发WalkLeft动画;GoRight激活的时候会触发WalkRight函数.
当没有激活态的输入时,我们会移除目标动画,让这个图保持一个自然的状态

if(orxInput_IsActive("GoRight"))
{
  orxObject_SetTargetAnim(pstSoldier, 
"WalkRight");
}

else if(orxInput_IsActive("GoLeft"))
{
  orxObject_SetTargetAnim(pstSoldier, 
"WalkLeft");
}

else
{
  orxObject_SetTargetAnim(pstSoldier, orxNULL);
}

就是这样!如何从任意当前动画切换到目标动画将会通过这个矢量图来计算。如果需要切换,他们将会自动播放。
注意:有很多的函数可以用高级的方法来控制动画,但是99%的时候,这两个函数是最常用的(orxObject_SetCurrentAnim() 和 orxObject_SetTargetAnim())。

让我们来看一下,动画是如何通知我们发生了什么的(比如,就像同步语音一样)。
首先,我们要向动画事件注册回调函数。

orxEvent_AddHandler(orxEVENT_TYPE_ANIM, EventHandler);

好了!让我们看下现在可以做什么了。
我们说我们想要打印出对象中哪个动画被播放、停止、剪切或者循环。需要写一下的回调函数。

orxSTATUS orxFASTCALL EventHandler(const orxEVENT *_pstEvent)
{
orxANIM_EVENT_PAYLOAD 
*pstPayload;
 
pstPayload 
= (orxANIM_EVENT_PAYLOAD *)_pstEvent->pstPayload;
 
switch(_pstEvent->eID)
{
  
case orxANIM_EVENT_START:
    orxLOG(
"Animation <%s>@<%s> has started!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient)));
    
break;
 
  
case orxANIM_EVENT_STOP:
    orxLOG(
"Animation <%s>@<%s> has stoped!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient)));
    
break;
 
  
case orxANIM_EVENT_CUT:
    orxLOG(
"Animation <%s>@<%s> has been cut!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient)));
    
break;
 
  
case orxANIM_EVENT_LOOP:
    orxLOG(
"Animation <%s>@<%s> has looped!", pstPayload->zAnimName, orxObject_GetName(orxOBJECT(_pstEvent->hRecipient)));
    
break;
  }

 
  
return orxSTATUS_SUCCESS;
}

先得到了事件的payload指针,因为我们只是在这里传递动画事件,所以我们可以安全的将payload 转化为orxANIM_EVENT_PAYLOAD类型,它在 orxAnim.h中定义。
如果我们在不同的事件(译者注:原文是even 根据上下文推断是作者拼写错误)类型中调用了同一个回调函数,我们首先将会查看是否得到了一个动画事件,可以这样做:

if(_pstEvent->eType == orxEVENT_TYPE_ANIM)

最后,事件接收者(_pstEvent→hRecipient)通常是播放动画的那个对象。将其用宏orxOBJECT()来转化为orOBJECT类型的对象。

现在让我们来看一眼数据方面的东西吧。
首先,我们需要定义一个动画集,它将会包含指定对象的动画的整个矢量图。
动画集在不会再内存中重复,并且它与矢量图相对应的多有动画和链路。
在上面这个例子中,我们又4个动画和10条可以用来切换的链路。

[AnimSet]
AnimationList 
= IdleRight#WalkRight#IdleLeft#WalkLeft
 
LinkList 
= IdleRightLoop#IdleRight2Left#IdleRight2WalkRight#WalkRightLoop#WalkRight2IdleRight#IdleLeftLoop#IdleLeft2Right#IdleLeft2WalkLeft#WalkLeftLoop#WalkLeft2IdleLeft

现在我们来开始定义动画!
在这之前,为了减少文章篇幅,我们将要使用orx 配置文件的集成特性。
先锚点的位置定义一项。
也许你可能在对象教程中看到了锚点的相关知识,锚点的位置信息将会匹配世界中的对象。如果没有确定的话,将会把左上角做为默认值。
锚点可以通过语义关键字来确定,如: top, bottom, center, left and right也可以通过实际的值来确定。

[Pivot]
Pivot 
= (15.031.00.0)

现在,我们来定义从锚点继承过来的图像对象。在我们这个例子中,它是一个位图,,包含了对象中所有的帧。因此基本的属性就是 位图文件的名字和一帧的大小。

[FullGraphic@Pivot]
Texture     
= ../../data/anim/soldier_full.png
TextureSize 
= (32320)

创建帧的准备工作都做好了。
让我们定义所有都是right-oriented的动画。一共6个。

[AnimRight1@FullGraphic]
TextureCorner 
= (000)
 
[AnimRight2@FullGraphic]
TextureCorner 
= (0320)
 
[AnimRight3@FullGraphic]
TextureCorner 
= (0640)
 
[AnimRight4@FullGraphic]
TextureCorner 
= (3200)
 
[AnimRight5@FullGraphic]
TextureCorner 
= (32320)
 
[AnimRight6@FullGraphic]
TextureCorner 
= (32640)


看到了吧,他们全都继承于FullGraphic,唯一能区分他们的属性就是TextureCorner. 好,我们已经定义完了所有的图形对象(他们载入的时候会转变为orxGraphic结构),下面定义动画本身。让我们从ideright动画开始说起,它包含一个单帧并持续0.1秒。

[IdleRight]
KeyData1      
= AnimRight6
KeyDuration1  
= 0.1


太简单了,来尝试下第二个:

[WalkRight]
DefaultKeyDuration  
= 0.1
KeyData1            
= AnimRight1
KeyData2            
= AnimRight2
KeyData3            
= AnimRight3
KeyData4            
= AnimRight4
KeyData5            
= AnimRight5
KeyData6            
= AnimRight6


当我们使用DefaultKeyDuration属性同时为所有的帧定义时并不是很难。我们可以像idleright动画中所做的那样,通过一个确定的键值来覆盖任意一帧。我们如法炮制做出left-oriented动画。通常我们使用翻转图形对象时,我们将会在代码运行中做这件事。但是那不是我们的目的!让我们来用与前面那个完全不同的方法来实现它!只有链路没有提到了让我们添上它。基本的链路结构非常简单,我们指定源动画和目的动画。

[IdleRightLoop]
Source      
= IdleRight
Destination 
= IdleRight

这里,我们有跟之前一样的基本信息,但是多了一个immediate属性做为键值。这就是说,当我们处于IdleRight动画时,并且目标是WalkRight,我们不必等到IdleRight完成,将直接完成这个动作,这就给了我们一个剪切动画的方法。 正如在代码中看到的一样。当我们已经开始行走的时候,没有显式的调用空闲动画,这是怎么做到的?看下从WalkRight到IdleRight的链路。

[IdleRight2WalkRight]
Source      
= IdleRight
Destination 
= WalkRight
Property    
= immediate


当我们再WalkRight状态并且移除了目标动画,引擎不得按照自然的路线走下去。这个意思是说,它会选取高优先级的链路。默认的优先级是8,它的范围是0到15.在这里,优先级是9,也就是说当我们没有目标的时候,就会选取它。它将会带我们回到IdleRight状态。这里也加了immdiate属性,这样,我们就不必等“走”这个循环完事再回到“空闲”
 
注意:这只是一个非常基本的图,用来阐述基本的动画切换过程,但是这个系统的扩展性很高。比如假设这样一个场景:你想从坐的状态变为走的状态,中间没有别的过度。随着游戏的开发,你可能觉得在这两个状态间加一个站立的状态会比较好。这时,你只需要再配置文件中添加这多出来的一步,而整个代码文件都不用更改。

资源:

源文件: 04_Anim.c
配置文件: 04_Anim.ini
posted @ 2010-07-05 23:21 IT菜鸟 阅读(534) | 评论 (1)编辑 收藏

仅列出标题
共7页: 1 2 3 4 5 6 7