mysql point2d_spriter动画编辑器的cocos2d

目前我的cocos2d-x编辑器的动画部分接口采用的是spriter动画编辑器提供的接口,spriter动画编辑器虽然简陋,但一般的需求基本上能够满足。可以在http://www.brashmonkey.com/spriter.htm下载,另外cocos2d-x的接口可以在论坛http://www.brashmonkey.com/foru

目前我的cocos2d-x编辑器的动画部分接口采用的是spriter动画编辑器提供的接口,spriter动画编辑器虽然简陋,但一般的需求基本上能够满足。可以在http://www.brashmonkey.com/spriter.htm下载,另外cocos2d-x的接口可以在论坛http://www.brashmonkey.com/forum/viewtopic.php?f=3&t=870下载。

接口导入进来之后,一切都很正常,然而在采用该接口进行开发时,发现有几个比较严重的问题。

问题1:资源采用多文件夹形式存储,渲染效率低

问题2:一个png只能有一个ccsprite,在编辑器是正常的,然而采用论坛那个接口导入到cocos2d-x就有问题。

问题3:精灵不能flip

问题4:不支持scale变换

陆续修改了上述4个问题

资源采用Sprite Frame实现

png的bug线性实现,时间复杂度O(1)

flip的实现参考cocos2d-x源码中CCTransitionFlipX的实现——摄像头换位

scale参考angle的变换就OK了

代码如下:

(代码中掺杂了编辑器部分的代码,精力有限,本代码仅供参考)

头文件:

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

//

//SCMLAnimator : KickStarter project Spriter renderer for cocos2d-x.

//

//Spriter website : http://www.kickstarter.com/projects/539087245/spriter

//

//Licensed under the BSD license, see LICENSE in root for details.

//

//Copyright (c) 2012 James Hui (a.k.a. Dr.Watson)

//

//For latest updates, please visit http://jameshui.com

//

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

#ifndef _CC_SPRITER_X_H_

#define _CC_SPRITER_X_H_

#include #include #include "JEvent.h"

#include "cocos2d.h"

#include "TouchSprite.h"

#include "tinyxml.h"

class CCSpriterX;

#define FILE_SPRITE_SIZE 128

namespace SCMLHelper

{

struct File

{

File();

~File();

void Init(TiXmlNode *node);

int id;

std::string name;

float width;

float height;

//一个文件可能有多个关联

cocos2d::CCSprite* sprites[FILE_SPRITE_SIZE];

};

class Folder

{

public:

Folder();

~Folder();

void Init(TiXmlNode *node);

int GetFileCount();

File *GetFile(int index);

private:

int mId;

std::string mName;

std::vector mFiles;

};

struct ObjectRef

{

void Init(TiXmlNode *node);

int id;

int timeline;

int key;

int z_index;

};

struct Object

{

void Init(TiXmlNode *node, CCSpriterX *animator, int timelineId);

int folder;

int file;

float x;

float y;

float angle;

float scaleX;

float scaleY;

float pivot_x;

float pivot_y;

int z_index;

cocos2d::CCSprite *sprite;

};

class Key

{

public:

Key();

~Key();

void Init(TiXmlNode *node, CCSpriterX *animator, int timelineId);

int GetObjectRefCount();

ObjectRef *GetObjectRef(int index);

int GetObjectCount();

Object *GetObject(int index);

float GetTime();

bool IsSpinCounterClockwise();

private:

int mId;

float mTime;

bool mSpinCounterClockwise;

std::vector mObjects;

std::vector mObjectRefs;

// will have bones later

};

class Timeline

{

public:

Timeline();

~Timeline();

void Init(TiXmlNode *node, CCSpriterX *animator);

int GetKeyframeCount();

Key *GetKeyframe(int index);

private:

int mId;

std::vector mKeyframes;

};

class Animation

{

public:

CC_SYNTHESIZE(JEvent *, event, Event);

CC_SYNTHESIZE(std::string, afterAction, AfterAction);

CCSpriterX * spr;

public:

void Restart();

Animation(CCSpriterX * spr);

~Animation();

void Update(float dt);

void Init(TiXmlNode *node, CCSpriterX *animator);

void Render();

bool IsDone();

public:

std::string getName(){return mName;}

private:

int mId;

std::string mName;

float mLength;

bool mLooping;

bool mDone;

Timeline *mMainline;

int mCurrKeyframe;

std::vector mTimelines;

float mTimer;

cocos2d::CCPoint mPosition;

};

class Entity

{

private:

CCSpriterX * spr;

public:

Entity(CCSpriterX * spr);

~Entity();

void StartWithEvent(const char * name, JEvent * event);

std::string CurrentAction();

void Update(float dt);

void Render();

void Start(const char * name,const char* _afterAction);

void SetId(int id);

void SetName(const char *name);

void AddAnimation(Animation *animation);

void NextAnimation();

private:

int mId;

std::string mName;

std::vector mAnimations;

int mCurrAnimation;

};

}

class CCSpriterX : public TouchSprite

{

private:

struct Pit{

int fileId;

int id;

int folderId;

};

Pit fileSprites[FILE_SPRITE_SIZE];

CC_SYNTHESIZE(ccColor3B, colorX, ColorX);

public:

CCSpriterX();

~CCSpriterX();

virtual void setFlipX(bool bFlipX);

bool initWithFile(const char *filename);

std::string CurrentAction();

virtual void draw(void);

virtual void update(float dt);

static CCSpriterX * create(const char *filename);

cocos2d::CCSprite * getSprite(int folderId, int fileId, int timelineId);

void PlayNext();

void PlayWithEvent(const char * name, JEvent * event);

void Play(const char* name,const char* _afterAction = 0);

private:

std::vector mFolders;

std::vector mEntities;

int mCurrEntity;

};

#endif

源文件:

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

//

//CCSpriterX : KickStarter project Spriter renderer for cocos2d-x.

//

//Spriter website : http://www.kickstarter.com/projects/539087245/spriter

//

//Licensed under the BSD license, see LICENSE in root for details.

//

//Copyright (c) 2012 James Hui (a.k.a. Dr.Watson)

//

//For latest updates, please visit http://jameshui.com

//

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

#include "CCSpriterX.h"

#include "Common.h"

#include "jerror.h"

USING_NS_CC;

namespace SCMLHelper

{

///

File::File()

{

for(size_t i=0; irelease();

}

}

}

void File::Init(TiXmlNode *node)

{

TiXmlElement *element = node->ToElement();

if (element)

{

int intValue;

float floatValue;

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

id = intValue;

else

id = 0;

name = element->Attribute("name");

if (element->QueryFloatAttribute("width", &floatValue) == TIXML_SUCCESS)

width = floatValue;

else

width = 0;

if (element->QueryFloatAttribute("height", &floatValue) == TIXML_SUCCESS)

height = floatValue;

else

height = 0;

if (name.size()>0)

{

//资源全部放到scml目录中!

std::string path = workPath+gConfig->read("res")+"/";

//sprite = CCSprite::create((path+"scml/"+name).c_str());

sprites[0] = CCSprite::createWithSpriteFrameName(name.c_str());

sprites[0]->retain();

}

}

}

///

Folder::Folder()

: mId(0)

{

mFiles.reserve(50);

}

Folder::~Folder()

{

int count = mFiles.size();

for (int i=0;iToElement();

if (element)

{

int intValue;

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

mId= intValue;

mName = element->Attribute("name")==0?".":element->Attribute("name");

for (TiXmlNode* fileNode = node->FirstChild(); fileNode; fileNode = fileNode->NextSibling())

{

File *file = new File();

file->Init(fileNode);

mFiles.push_back(file);

}

}

}

///

void ObjectRef::Init(TiXmlNode *node)

{

TiXmlElement *element = node->ToElement();

if (element)

{

int intValue;

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

id = intValue;

else

id = 0;

if (element->QueryIntAttribute("timeline", &intValue) == TIXML_SUCCESS)

timeline = intValue;

else

timeline = 0;

if (element->QueryIntAttribute("key", &intValue) == TIXML_SUCCESS)

key = intValue;

else

key = 0;

if (element->QueryIntAttribute("z_index", &intValue) == TIXML_SUCCESS)

z_index = intValue;

else

z_index = 0;

}

}

///

void Object::Init(TiXmlNode *node, CCSpriterX *animator, int timelineId)

{

sprite = NULL;

float scaleFactor = CCDirector::sharedDirector()->getContentScaleFactor();

TiXmlElement *element = node->ToElement();

if (element)

{

int intValue;

float floatValue;

if (element->QueryIntAttribute("folder", &intValue) == TIXML_SUCCESS)

folder = intValue;

else

folder = 0;

if (element->QueryIntAttribute("file", &intValue) == TIXML_SUCCESS)

file = intValue;

else

file = 0;

if (element->QueryFloatAttribute("x", &floatValue) == TIXML_SUCCESS)

x = floatValue/scaleFactor;

else

x = 0;

if (element->QueryFloatAttribute("y", &floatValue) == TIXML_SUCCESS)

y = floatValue/scaleFactor;

else

y = 0;

if (element->QueryFloatAttribute("angle", &floatValue) == TIXML_SUCCESS)

angle = floatValue;

else

angle = 0;

if (element->QueryFloatAttribute("scale_x", &floatValue) == TIXML_SUCCESS)

scaleX = floatValue;

else

scaleX = 1;

if(scaleX < 1){

jlog("shit");

}

if (element->QueryFloatAttribute("scale_y", &floatValue) == TIXML_SUCCESS)

scaleY = floatValue;

else

scaleY = 1;

if (element->QueryFloatAttribute("pivot_x", &floatValue) == TIXML_SUCCESS)

pivot_x = floatValue;

else

pivot_x = 0;

if (element->QueryFloatAttribute("pivot_y", &floatValue) == TIXML_SUCCESS)

pivot_y = floatValue;

else

pivot_y = 1;

if (element->QueryIntAttribute("z_index", &intValue) == TIXML_SUCCESS)

z_index = intValue;

else

z_index = 0;

sprite = animator->getSprite(folder, file, timelineId);

}

}

///

Key::Key()

: mId(0)

, mTime(0)

, mSpinCounterClockwise(true)

{

mObjects.reserve(50);

mObjectRefs.reserve(50);

}

Key::~Key()

{

int count = mObjects.size();

for (int i=0;iToElement();

if (element)

{

int intValue;

float floatValue;

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

mId = intValue;

float time = 0;

if (element->QueryFloatAttribute("time", &floatValue) == TIXML_SUCCESS)// was in milliseconds, convert to seconds instead

time = floatValue/1000.0f;

mTime = time;

if (element->QueryIntAttribute("spin", &intValue) == TIXML_SUCCESS)

mSpinCounterClockwise = !(intValue == -1);

for (TiXmlNode* objNode = node->FirstChild(); objNode; objNode = objNode->NextSibling())

{

element = objNode->ToElement();

const char *tabObj = element->Value();

if (strcmp(tabObj, "object_ref")==0)

{

ObjectRef *ref = new ObjectRef();

ref->Init(objNode);

mObjectRefs.push_back(ref);

}

else if (strcmp(tabObj, "object")==0)

{

Object *obj = new Object();

obj->Init(objNode, animator, timelineId);

mObjects.push_back(obj);

}

}

}

}

///

Timeline::Timeline()

: mId(0)

{

mKeyframes.reserve(50);

}

Timeline::~Timeline()

{

int count = mKeyframes.size();

for (int i=0;iToElement();

if (element)

{

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

mId = intValue;

for (TiXmlNode* keyNode = node->FirstChild(); keyNode; keyNode = keyNode->NextSibling())

{

element = keyNode->ToElement();

if (element)

{

Key *keyframe = new Key();

keyframe->Init(keyNode, animator, mId);

mKeyframes.push_back(keyframe);

}

}

}

}

///

Animation::Animation(CCSpriterX * _spr)

: mId(0)

, spr(_spr)

, mCurrKeyframe(0)

, mMainline(NULL)

, mDone(false)

, mTimer(0)

,event(0),afterAction("")

{

mTimelines.reserve(50);

}

Animation::~Animation()

{

int count = mTimelines.size();

for (int i=0;iToElement();

if (element)

{

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

mId = intValue;

mName = element->Attribute("name");

if (element->QueryFloatAttribute("length", &floatValue) == TIXML_SUCCESS)

mLength = floatValue/1000.0f;// was in milliseconds, convert to seconds instead

const char *looping = element->Attribute("looping");// was set to "false" in alpha, but in fact looping all the time

mLooping = true;

for (TiXmlNode* lineNode = node->FirstChild(); lineNode; lineNode = lineNode->NextSibling())

{

element = lineNode->ToElement();

const char *tabLine = element->Value();

if (strcmp(tabLine, "mainline")==0)// 1 mainline only

{

mMainline = new Timeline();

mMainline->Init(lineNode, animator);

}

else if (strcmp(tabLine, "timeline")==0)

{

Timeline *timeline = new Timeline();

timeline->Init(lineNode, animator);

mTimelines.push_back(timeline);

}

}

}

}

bool Animation::IsDone()

{

return mDone;

}

void Animation::Restart()

{

mDone = false;

mTimer = 0;

mCurrKeyframe = 0;

}

float lerp(float a, float b, float t){

return a+(b-a)*t;

}

void Animation::Update(float dt)

{

mTimer += dt;

if (mTimer >= mLength)

{

mDone = true;

Restart();// always looping for now

}

int count = mMainline->GetKeyframeCount();

Key *keyframe = mMainline->GetKeyframe(mCurrKeyframe);

float currTime = keyframe->GetTime();

Key *keyframeNext = NULL;

int next = mCurrKeyframe+1;

if (next > count-1)// looping

next = 0;

keyframeNext = mMainline->GetKeyframe(next);

if (keyframeNext)

{

float nextTime = keyframeNext->GetTime();

if (next == 0)

nextTime = mLength;

if (mTimer >= nextTime)

{

mCurrKeyframe = next;

keyframe = keyframeNext;

currTime = keyframe->GetTime();

next = mCurrKeyframe+1;

if (next > count-1)// looping

next = 0;

keyframeNext = mMainline->GetKeyframe(next);

if (keyframeNext == NULL)

return;

nextTime = keyframeNext->GetTime();

if (next == 0)

nextTime = mLength;

}

float t = (mTimer-currTime)/(nextTime-currTime);

int count = keyframe->GetObjectRefCount();

for (int i=0;iGetObjectRef(i);

ObjectRef *refNext = keyframeNext->GetObjectRef(i);

if (ref && refNext)

{

Key *keyRef = mTimelines[ref->timeline]->GetKeyframe(ref->key);

Object *obj = keyRef->GetObject(0);// should be only 1 object

Key *keyRefNext = mTimelines[refNext->timeline]->GetKeyframe(refNext->key);

Object *objNext = keyRefNext->GetObject(0);

float x = lerp(obj->x, objNext->x, t);

float y = lerp(obj->y, objNext->y, t);

float scaleX = lerp(obj->scaleX, objNext->scaleX, t);

float scaleY = lerp(obj->scaleY, objNext->scaleY, t);

float angle = objNext->angle-obj->angle;

if (keyRef->IsSpinCounterClockwise())

{

if (angle < 0)

angle = (objNext->angle+360)-obj->angle;

}

else

{

if (angle > 0)

{

angle = (objNext->angle-360)-obj->angle;

}

}

if (ref->timeline != refNext->timeline)

t = 0;

angle = obj->angle+(angle)*t;

if (angle >= 360)

angle -= 360;

float px = obj->pivot_x+(objNext->pivot_x-obj->pivot_x)*t;

float py = obj->pivot_y+(objNext->pivot_y-obj->pivot_y)*t;

CCPoint newPos = ccp(x, y);

obj->sprite->setPosition(newPos);

obj->sprite->setRotation(-angle);

obj->sprite->setScaleX(scaleX);

obj->sprite->setScaleY(scaleY);

obj->sprite->setAnchorPoint(ccp(px, py));

}

}

}

}

void Animation::Render()

{

Key *keyframe = mMainline->GetKeyframe(mCurrKeyframe);

int count = keyframe->GetObjectRefCount();

for (int i=0;iGetObjectRef(i);

if (ref)

{

Key *keyRef = mTimelines[ref->timeline]->GetKeyframe(ref->key);

Object *obj = keyRef->GetObject(0);// should be only 1 object

obj->sprite->setColor(spr->getColorX());

obj->sprite->visit();

}

}

}

///

Entity::Entity(CCSpriterX * _spr)

: mCurrAnimation(0)

, mId(0)

, spr(_spr)

{

mAnimations.reserve(50);

};

Entity::~Entity()

{

int count = mAnimations.size();

for (int i=0;iUpdate(dt);

}

std::string Entity::CurrentAction()

{

return mAnimations[mCurrAnimation]->getName();

}

void Entity::StartWithEvent(const char * name, JEvent * event)

{

for(size_t i=0; igetName() == std::string(name)){

mCurrAnimation = i;

mAnimations[i]->setEvent(event);

mAnimations[i]->Restart();

}

}

}

void Entity::Start(const char * name, const char * _afterAction)

{

for(size_t i=0; igetName() == std::string(name)){

mCurrAnimation = i;

mAnimations[i]->setAfterAction(_afterAction);

mAnimations[i]->Restart();

}

}

}

void Entity::Render()

{

Animation *animation = mAnimations[mCurrAnimation];

animation->Render();

}

void Entity::NextAnimation()

{

mCurrAnimation++;

if (mCurrAnimation >= (int)mAnimations.size())

mCurrAnimation = 0;

Animation *animation = mAnimations[mCurrAnimation];

animation->Restart();

}

void Entity::SetId(int id)

{

mId = id;

}

void Entity::SetName(const char *name)

{

mName = name;

}

void Entity::AddAnimation(Animation *animation)

{

mAnimations.push_back(animation);

}

}

///

using namespace SCMLHelper;

CCSpriterX::CCSpriterX()

{

mFolders.reserve(50);

mEntities.reserve(50);

for(int i=0; itype = SCML;

animator->m_state = kLivingStateUngrabbed;

if (animator && animator->initWithFile(filename))

{

//由于是动画层,没有大小,这里设置大小,使之能够拾取

animator->setContentSize(CCSizeMake(100, 100));

animator->autorelease();

return animator;

}

CC_SAFE_DELETE(animator);

return NULL;

}

void CCSpriterX::update(float dt)

{

if (dt > 0.0167f)

dt = 0.0167f;

Entity *entity = mEntities[mCurrEntity];

entity->Update(dt);

}

void CCSpriterX::draw(void)

{

Entity *entity = mEntities[mCurrEntity];

entity->Render();

}

std::string CCSpriterX::CurrentAction()

{

Entity *entity = mEntities[mCurrEntity];

return entity->CurrentAction();

}

void CCSpriterX::PlayWithEvent(const char * name, JEvent * event)

{

Entity *entity = mEntities[mCurrEntity];

entity->StartWithEvent(name, event);

}

void CCSpriterX::Play(const char* name, const char * _afterAction)

{

Entity *entity = mEntities[mCurrEntity];

entity->Start(name, _afterAction == 0?"":_afterAction);

}

void CCSpriterX::PlayNext()

{

Entity *entity = mEntities[mCurrEntity];

entity->NextAnimation();

}

CCSprite *CCSpriterX::getSprite(int folderId, int fileId, int timelineId)

{

if (folderId < (int)mFolders.size())

{

Folder *folder = mFolders[folderId];

if (folder)

{

File *file = folder->GetFile(fileId);

if (file){

int id = this->fileSprites[timelineId].id;

if(id == -1){

for(int i=0; isprites[id] == 0){

file->sprites[id] = CCSprite::createWithSpriteFrameName(file->name.c_str());

file->sprites[id]->retain();

}

return file->sprites[id];

}

}

}

return NULL;

}

void CCSpriterX::setFlipX(bool bFlipX)

{

if(bFlipX != m_bFlipX){

float ex,ey,ez;

this->getCamera()->getEyeXYZ(&ex, &ey, &ez);

this->getCamera()->setEyeXYZ(ex, ey, bFlipX?-fabs(ez):fabs(ez));

this->m_bFlipX = bFlipX;

}

}

bool CCSpriterX::initWithFile(const char *filename)

{

char cfilename[256];

strcpy(cfilename, filename);

string name = strtok(cfilename, ".");

string suffix = strtok(cfilename, ".");

mCurrEntity = 0;

unsigned long filesize;

string path = workPath+gConfig->read("res")+"/"+filename;

char *buffer = (char *)CCFileUtils::sharedFileUtils()->getFileData(path.c_str(), "rb", &filesize);

if (buffer == NULL)

return false;

//加载大图

string sfplist = workPath+gConfig->read("res")+"/scml/"+name+".plist";

string sfpng = workPath+gConfig->read("res")+"/scml/"+name+".png";

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(sfplist.c_str(), sfpng.c_str());

TiXmlDocument doc;

doc.Parse(buffer);

TiXmlNode *root = doc.FirstChild("spriter_data");

if (root)

{

TiXmlElement *element = root->ToElement();

const char *version = element->Attribute("scml_version");

const char *generator = element->Attribute("generator");

const char *generatorVersion = element->Attribute("generator_version");

for (TiXmlNode* entityNode = root->FirstChild(); entityNode; entityNode = entityNode->NextSibling())

{

element = entityNode->ToElement();

if (element)

{

const char *tab = element->Value();

if (strcmp(tab, "folder")==0)

{

Folder *folder = new Folder();

folder->Init(entityNode);

mFolders.push_back(folder);

}

else if (strcmp(tab, "entity")==0)

{

int intValue;

Entity *entity = new Entity(this);

if (element->QueryIntAttribute("id", &intValue) == TIXML_SUCCESS)

entity->SetId(intValue);

entity->SetName(element->Attribute("name"));

for (TiXmlNode* animationNode = entityNode->FirstChild(); animationNode; animationNode = animationNode->NextSibling())

{

Animation *animation = new Animation(this);

animation->Init(animationNode, this);

entity->AddAnimation(animation);

}

mEntities.push_back(entity);

}

}

}

}

CC_SAFE_DELETE_ARRAY(buffer);

this->scheduleUpdate();

return true;

}

f68f2add0b68e4f9810432fce46917b7.png

本文原创发布php中文网,转载请注明出处,感谢您的尊重!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值