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

2024-04-16 18:08

本文主要是介绍C 语言链式调用与Tween算法实现(4)链式封装接口,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在有上下文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 30static 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];


这篇关于C 语言链式调用与Tween算法实现(4)链式封装接口的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/909520

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll