您的位置:首页 > 其它

C 语言链式调用与Tween算法实现(4)链式封装接口

2016-12-07 21:52 417 查看
在有上下文this指针的语言中,可以把this指针return出去。这样就可以形成一种链式调用的效果。配合上良好的方法命名,能够让函数功能的组合调用,使用起来非常的直觉化。在C语言中并没有this指针,所有的上下文对象需要,手动传入上下文对象。

以前介绍了3篇关于C语言如何实现tween缓动算法的。

C 实现通用Tween缓动动画(1)插值公式

C 实现通用Tween缓动动画(2)Tween数据结构

然而,函数的调用缺少链式调用非常的不流畅。而tween又很多默认参数的设定,往往链式调用能够更加方便的使用。

本文结合一种实现链式调用的方式,来在tween的实现之上进行一个链式调用的封装。我的思路很简单,链式调用需要上下文,C没有,那我们就要构造一个上下文以供使用。那我们看代码。

struct ATweenTool
{

/**
* Create one TweenAction in context for chain setting
*/
struct ATweenTool* (*SetAction)       ();

/**
* Create action with no actionValue
* just through duration time then callback
*/
struct ATweenTool* (*SetInterval)     (float duration);

//--------------------------------------------------------------------------------------------------
// Create one TweenAction with TweenActionValue
//--------------------------------------------------------------------------------------------------

struct ATweenTool* (*SetActionMoveX)  (float moveX,   float duration);
struct ATweenTool* (*SetActionMoveY)  (float moveY,   float duration);
struct ATweenTool* (*SetActionMove)   (float moveX,   float moveY,  float duration, bool isRelative, TweenEaseType easeType);
struct ATweenTool* (*SetActionScaleX) (float scaleX,  float duration);
struct ATweenTool* (*SetActionScaleY) (float scaleY,  float duration);
struct ATweenTool* (*SetActionScale)  (float scaleX,  float scaleY, float duration, bool isRelative, TweenEaseType easeType);
struct ATweenTool* (*SetActionRotateZ)(float rotateZ, float duration);
struct ATweenTool* (*SetActionFadeTo) (float fadeTo,  float duration);

//--------------------------------------------------------------------------------------------------
// After SetAction then set TweenAction property for current context created
//--------------------------------------------------------------------------------------------------

struct ATweenTool* (*SetDuration)     (float                     duration);
struct ATweenTool* (*SetUserData)     (void*                     userData);
struct ATweenTool* (*SetQueue)        (bool                      isQueue);
struct ATweenTool* (*SetOnComplete)   (TweenActionOnComplete     OnComplete);
struct ATweenTool* (*SetTarget)       (void*                     target);

/**
* Get TweenAction in current context
*/
struct ATweenTool* (*GetAction)       (TweenAction**             outActionPtr);

//--------------------------------------------------------------------------------------------------
// Add new TweenActionValue into context TweenAction
//--------------------------------------------------------------------------------------------------

struct ATweenTool* (*SetMoveX)        (float                     moveX);
struct ATweenTool* (*SetMoveY)        (float                     moveY);
struct ATweenTool* (*SetScaleX)       (float                     scaleX);
struct ATweenTool* (*SetScaleY)       (float                     scaleY);
struct ATweenTool* (*SetRotateZ)      (float                     rotateZ);
struct ATweenTool* (*SetFadeTo)       (float                     fadeTo);

//--------------------------------------------------------------------------------------------------
// After SetValue then set TweenActionValue property for current context created
//--------------------------------------------------------------------------------------------------

/**
* After SetValue then SetRelative on new created TweenActiveValue
*/
struct ATweenTool* (*SetRelative)     (bool                      isRelative);

/**
* After SetValue then SetEaseType on new created TweenActiveValue
*/
struct ATweenTool* (*SetEaseType)     (TweenEaseType             easeType);

//--------------------------------------------------------------------------------------------------

/**
* Run actions all in current context, set action target if has actionValue
* and use target be tweenId
*/
void               (*RunActions)      (void*                      target);

/**
* Run actions all in current context, action must set target
* if has actionValue and return tweenId
*/
void*              (*RunTargets)      ();
};

extern struct ATweenTool ATweenTool[1];


我们需要一个链式调用的发起者,通常是一个单例对象。这里就是有ATweenTool充当。可以看到,几乎所有的函数都会直接返回 ATweenTool结构指针,这样就可以在函数调用完成的时候,链式的调用所属的其它函数形成链式调用。ATweenTool的方法有3大类。

第一类,生成一个Action,以SetAction开头的函数以及SetInterval函数,都会生成一个上下文Action对象。以后针对Action属性的设置函数,比如SetDuration,SetUserData等等,都会使用这个上下文的Action,可以看到这些函数并没有传入Action对象作为参数。当重新SetAction的时候,上下文对象就会重置。

第二类,生成一个ActionValue,以SetMove,SetScale等等这样的函数,都会生成一个上下文的ActionValue对象。以后针对ActionValue属性的设置函数,比如SetRelative等函数,都会使用这个上下文的ActionValue,可以看到这些函数并没有传入ActionValue作为参数。 当重新SetMove等时候,上下文对象就会重置。

第三类,RunActions和RunTargets两个函数,返回值不再是ATweenTool这样返回。意味着这是链式调用的终点。一旦调用Tween动画开始执行。

接下来,看看上下文是如何生成的。

#define action_length 30

static Array(TweenAction*) actionArr[1] =
{
(TweenAction*[action_length]) {},
0
};

static TweenAction*      action      = NULL;
static TweenActionValue* actionValue = NULL;


就是简单的使用了一个static变量持有了Action数组。也就是说上下文Action的生成是有上限的。在执行tween动画执行之前,一共只能生成action_length个Action。那么,action和actionValue变量,就是记录了当前上下文的对象。随着新Action和ActionValue的设置,当前变量不断的被赋值。

#define CheckAction(tag) \
ALogA(action      != NULL, "ATweenTool " tag " TweenAction not created");

#define CheckActionValue(tag) \
ALogA(actionValue != NULL, "ATweenTool " tag " TweenActionValue invalid");

static struct ATweenTool* SetAction()
{
ALogA
(
actionArr->length <= action_length,
"ATweenTool can not cache TweenActions = %d more than %d",
actionArr->length,
action_length
);

action      = ATween->GetAction();
actionValue = NULL;

AArraySet
(
actionArr,
actionArr->length++,
action,
TweenAction*
);

return ATweenTool;
}

static inline struct ATweenTool* SetValue(TweenActionValueGetSet* valueGetSet, float value)
{
CheckAction("SetValue");

actionValue        = ATween->AddTweenActionValue(action);
actionValue->OnGet = valueGetSet->OnGet;
actionValue->OnSet = valueGetSet->OnSet;
actionValue->value = value;

return ATweenTool;
}

static struct ATweenTool* SetDuration(float duration)
{
CheckAction("SetDuration");
action->duration = duration;

return ATweenTool;
}

static struct ATweenTool* SetRelative(bool isRelative)
{
CheckAction     ("SetRelative");
CheckActionValue("SetRelative");
actionValue->isRelative = isRelative;

return ATweenTool;
}

static struct ATweenTool* SetEaseType(TweenEaseType easeType)
{
CheckAction     ("SetEaseType");
CheckActionValue("SetEaseType");
actionValue->easeType = easeType;

return ATweenTool;
}

static struct ATweenTool* SetInterval(float duration)
{
SetAction();
SetDuration(duration);

return ATweenTool;
}

//--------------------------------------------------------------------------------------------------

static struct ATweenTool* SetActionMoveX(float moveX, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->moveX, moveX);
SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionMoveY(float moveY, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->moveY, moveY);
SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionMove(float moveX, float moveY, float duration, bool isRelative, TweenEaseType easeType)
{
SetAction  ();
SetValue   (ATweenActionValueGetSetImpl->moveX, moveX);
SetRelative(isRelative);
SetEaseType(easeType);

SetValue   (ATweenActionValueGetSetImpl->moveY, moveY);
SetRelative(isRelative);
SetEaseType(easeType);

SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionScaleX(float scaleX, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->scaleX, scaleX);
SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionScaleY(float scaleY, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->scaleY, scaleY);
SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionScale(float scaleX, float scaleY, float duration, bool isRelative, TweenEaseType easeType)
{
SetAction  ();
SetValue   (ATweenActionValueGetSetImpl->scaleX, scaleX);
SetRelative(isRelative);
SetEaseType(easeType);

SetValue   (ATweenActionValueGetSetImpl->scaleY, scaleY);
SetRelative(isRelative);
SetEaseType(easeType);

SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionRotateZ(float rotateZ, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->rotateZ, rotateZ);
SetDuration(duration);

return ATweenTool;
}

static struct ATweenTool* SetActionFadeTo(float fadeTo, float duration)
{
SetAction();
SetValue(ATweenActionValueGetSetImpl->fadeTo, fadeTo);
SetDuration(duration);

return ATweenTool;
}

//--------------------------------------------------------------------------------------------------

static struct ATweenTool* SetUserData(void* userData)
{
CheckAction("SetUserData");
action->userData = userData;

return ATweenTool;
}

static struct ATweenTool* SetQueue(bool isQueue)
{
CheckAction("SetQueue");
action->isQueue = isQueue;

return ATweenTool;
}

static struct ATweenTool* SetOnComplete(TweenActionOnComplete OnComplete)
{
CheckAction("SetOnComplete");
action->OnComplete = OnComplete;

return ATweenTool;
}

static struct ATweenTool* SetTarget(void* target)
{
CheckAction("SetTarget");
action->target = target;

return ATweenTool;
}

static struct ATweenTool* GetAction(TweenAction** outActionPtr)
{
CheckAction("GetAction");
*outActionPtr = action;

return ATweenTool;
}

//--------------------------------------------------------------------------------------------------

static struct ATweenTool* SetMoveX(float moveX)
{
return SetValue(ATweenActionValueGetSetImpl->moveX, moveX);
}

static struct ATweenTool* SetMoveY(float moveY)
{
return SetValue(ATweenActionValueGetSetImpl->moveY, moveY);
}

static struct ATweenTool* SetScaleX(float scaleX)
{
return SetValue(ATweenActionValueGetSetImpl->scaleX, scaleX);
}

static struct ATweenTool* SetScaleY(float scaleY)
{
return SetValue(ATweenActionValueGetSetImpl->scaleY, scaleY);
}

static struct ATweenTool* SetRotateZ(float rotateZ)
{
return SetValue(ATweenActionValueGetSetImpl->rotateZ, rotateZ);
}

static struct ATweenTool* SetFadeTo(float fadeTo)
{
return SetValue(ATweenActionValueGetSetImpl->fadeTo, fadeTo);
}

//--------------------------------------------------------------------------------------------------

static void RunActions(void* target)
{
ALogA(target, "RunActions, target must not NULL");

for (int i = 0; i < actionArr->length; i++)
{
TweenAction* action = AArrayGet(actionArr, i, TweenAction*);

if (action->actionValueList->size > 0)
{
action->target = target;
}
}

ATween->RunActions(actionArr, &target);

actionArr->length = 0;
action            = NULL;
actionValue       = NULL;
}

static void* RunTargets()
{
for (int i = 0; i < actionArr->length; i++)
{
TweenAction* action = AArrayGet(actionArr, i, TweenAction*);

if (action->actionValueList->size > 0)
{
ALogA
(
action->target != NULL,
"RunActions, the {%d} action has actionValue, so must set target",
i
);
}
}

void* tweenId = NULL;

ATween->RunActions(actionArr, &tweenId);

actionArr->length = 0;
action            = NULL;
actionValue       = NULL;

return tweenId;
}

struct ATweenTool ATweenTool[1] =
{
SetAction,
SetInterval,

SetActionMoveX,
SetActionMoveY,
SetActionMove,

SetActionScaleX,
SetActionScaleY,
SetActionScale,

SetActionRotateZ,
SetActionFadeTo,

SetDuration,
SetUserData,
SetQueue,
SetOnComplete,
SetTarget,
GetAction,

SetMoveX,
SetMoveY,
SetScaleX,
SetScaleY,
SetRotateZ,
SetFadeTo,

SetRelative,
SetEaseType,

RunActions,
RunTargets,
};

#undef action_length
#undef CheckAction
#undef CheckActionValue


可以看到,所有链式调用的函数最后都会return ATweenTool结构。但执行tween动画以后,就会重置当前的上下文变量。那么,使用起来是一下这个样子的。

ATweenTool->SetActionMoveX(deltaX * 0.95f, enemy->hurtXTime)
->SetEaseType   (tween_ease_quad_in)
->SetQueue      (false)
->SetOnComplete (AttackOnComplete)
->SetUserData   (enemy)

->SetActionMoveY(0.03f, enemy->hurtYUpTime)
->SetEaseType   (tween_ease_quad_in)

->SetActionMoveY(-0.03f, enemy->hurtYDownTime)
->SetEaseType   (tween_ease_quad_out)

->RunActions    (enemyDrawable);


最后给出,额外辅助的结构。

typedef struct
{
TweenActionValueOnGet OnGet;
TweenActionValueOnSet OnSet;
}
TweenActionValueGetSet;

struct ATweenActionValueGetSetImpl
{
TweenActionValueGetSet moveX  [1];
TweenActionValueGetSet moveY  [1];

TweenActionValueGetSet scaleX [1];
TweenActionValueGetSet scaleY [1];

TweenActionValueGetSet rotateZ[1];
TweenActionValueGetSet fadeTo [1];
};

/**
* Application must implement tween action value get set method
*/
extern struct ATweenActionValueGetSetImpl ATweenActionValueGetSetImpl[1];
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐