粗略写了个Player类,用来测试人物的待机动作和奔跑动作的播放,以及两种动作的切换播放。
1,这里要用到plist文件,对于plist,我目前的理解就是:plist和xml很像,可以说就是xml, 不过在mac下面就成了plist,美术资源如果是一张有9个帧动作的图片,那么plist文件里就应该有9个dict节点来分别对应这9个动作,每个节点里的属性包括精灵大小、需要渲染的纹理矩形的左上角和右下角这两个点的坐标、坐标偏移值、精灵单张原始图片的大小...
最开始时是想通过setPostition和 setAnothorPoint来解决人物的原点位置设置,但发现可以直接在plist里设置offset偏移量来决定原点位置。
plist文件里的spriteOffset属性正好可以用来设置原点的位置。
这里给出我自己算出的spriteOffset坐标公式:
X = 人物单帧图片宽度的一半 减去 注册点到图片的右边距 (得到值可能是负数,就要负数,不要绝对值)
Y = 人物单帧图片高度的一半 减去 注册点到图片的底边
2,cocos2d-x真心很赞,图片纹理渲染优化都有处理,使用这种纹理缓存机制,可以避免重复渲染相同的纹理。
这里涉及到几个类:
CCSpriteBatchNode, CCSpriteFrameCache, CCSpriteFrame, CCAnimation
3, 代码中包括触屏之后除了人物的动作切换,还有位移、水平转向的处理,代码借鉴了官方test里的例子,贴心的2dx团队呀有么有!!!
4,新手要注意,draw方法不是主动去调用的,这里是重写了父类的draw方法,在这里绘制了一下人物的脚下画了个十字以以表示原点位置,方便测试,
ccDrawLine()方法就是直接调用openGL ES的接口在屏幕上绘制一条线。那么draw方法是被谁调用的呢,是cocos2d-x的UI主线程。
//
// Player.h
// PlayerActionTest
//
// Created by 嘉定 on 12-12-25.
//
//
#ifndef __PlayerActionTest__Player__
#define __PlayerActionTest__Player__
#include "cocos2d.h"
USING_NS_CC;
class Player : public CCSprite
{
public:
Player();
virtual ~Player();
virtual bool init();
virtual void draw();
typedef enum
{
state_idle = 1,
state_run = 2,
state_sit = 3,
state_UnKnown = 4
} ActionState;
typedef enum
{
headward_left = 1,
headward_right = 2,
headward_Unknow = 3
}HeadwardType;
//跑向一个点,达到目标后是否回调
void runTo(CCPoint targetPos,bool callback);
//播放某个动作动画
void playAction(ActionState state);
//当前动作状态
ActionState currentActionState;
//当前朝向
HeadwardType currentHeadward;
CREATE_FUNC(Player);
private:
//动画批处理节点
CCSpriteBatchNode *idleSpriteBatch;
CCSpriteBatchNode *runSpriteBatch;
//侦动画缓存
CCSpriteFrameCache *idleFrameCache;
CCSpriteFrameCache *runFrameCache;
CCSprite *idleSprite;
CCSprite *runSprite;
void stopCurrentAction();
//播放待机动画
void idle();
//播放奔跑动画
void run();
//跑到目标点之后回调
//handler
void runToCallBack();
};
#endif /* defined(__PlayerActionTest__Player__) */
//
// Player.cpp
// PlayerActionTest
//
// Created by 嘉定 on 12-12-25.
//
//
#include "Player.h"
Player::Player():
idleFrameCache(NULL),
runFrameCache(NULL),
idleSpriteBatch(NULL),
runSpriteBatch(NULL),
idleSprite(NULL),
runSprite(NULL),
currentActionState(Player::state_UnKnown),
currentHeadward(Player::headward_Unknow)
{
}
Player::~Player()
{
}
bool Player::init()
{
if(!CCSprite::init())
{
return false;
}
//默认朝向右边
currentHeadward = headward_right;
//init total action FrameCache
idleFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
idleFrameCache->addSpriteFramesWithFile("player/heping.plist","player/heping.png");
runFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
runFrameCache->addSpriteFramesWithFile("player/pao.plist", "player/pao.png");
return true;
}
void Player::runTo(CCPoint targetPos,bool callback)
{
this->stopAllActions();
//目前用CCMoveTO,需要设定一个从当前位置到目标位置的移动时间,那么应提前算出移动所需的时间
const float SPEED = 12;
float playerX = this->getPosition().x;
float playerY = this->getPosition().y;
if(targetPos.x >= playerX)
{
this->currentHeadward = headward_right;
}
else
{
this->currentHeadward = headward_left;
}
this->playAction(state_run);
float disX = playerX - targetPos.x;
float disY = playerY - targetPos.y;
const float DIS = sqrtf(disX * disX + disY * disY);
float moveTime = (DIS / SPEED) / 60;
//但是人物的移动时间要按照从当前点到touch点的真实距离来计算时间
CCAction *action = NULL;
if(callback)
{
action = CCSequence::create(
CCMoveTo::create(moveTime, targetPos),
CCCallFunc::create(this, callfunc_selector(Player::runToCallBack)),
NULL);
}
else
{
action = CCSequence::create(CCMoveTo::create(moveTime, targetPos),NULL);
}
this->runAction(action);
//角度转向
//
// float at = (float) CC_RADIANS_TO_DEGREES(atanf(disX/disY));
//
// if(disX < 0)
// {
// if(disY < 0)
// at = 180 + fabs(at);
// else
// at = 180 - fabs(at);
// }
// this->runAction(CCRotateTo::create(1, at));
}
void Player::playAction(ActionState state)
{
if(currentActionState == state)
{
//这里逻辑有点细微,当正在向左跑时,如果点击了人物右边屏幕,那么不回重新创建一次奔跑的动画变量,但是人物时需要反转的
//idle状态不需要考虑这一点,跑到了就会idle,这时会自动根据之前奔跑的朝向来设定。
if(currentActionState == state_run)
{
if(runSprite->isFlipX() && currentHeadward == headward_right)
{
runSprite->setFlipX(false);
}
else if(!runSprite->isFlipX() && currentHeadward == headward_left)
{
runSprite->setFlipX(true);
}
}
return;
}
if(state == state_idle)
{
idle();
}
else if(state == state_run)
{
run();
}
else
{
}
}
void Player::draw()
{
ccDrawColor4B(255, 0, 0, 255);
glLineWidth(6.0f);
ccDrawLine(ccp(-20,0),ccp(20,0));
ccDrawLine(ccp(0,15),ccp(0,-15));
}
void Player::idle()
{
stopCurrentAction();
idleSprite = CCSprite::createWithSpriteFrameName("heping_01");
if(this->currentHeadward == headward_left)
{
idleSprite->setFlipX(true);
}
else if(this->currentHeadward == headward_right)
{
idleSprite->setFlipX(false);
}
CCSize idleSize = idleSprite->getContentSize();
idleSpriteBatch = CCSpriteBatchNode::create("player/heping.png");
idleSpriteBatch->addChild(idleSprite);
addChild(idleSpriteBatch);
//动画帧数组
CCArray *animatFrames = CCArray::createWithCapacity(8);
char str[100] = {0};
for(int i = 1;i < 8;++i)
{
sprintf(str, "heping_%02d",i);
CCSpriteFrame *frame = idleFrameCache->spriteFrameByName(str);
animatFrames->addObject(frame);
}
CCAnimation *animation = CCAnimation::createWithSpriteFrames(animatFrames,0.2f);
idleSprite->runAction(CCRepeatForever::create(CCAnimate::create(animation)));
currentActionState = state_idle;
}
void Player::run()
{
stopCurrentAction();
runSprite = CCSprite::createWithSpriteFrameName("run_01");
if(this->currentHeadward == headward_left)
{
runSprite->setFlipX(true);
}
else if(this->currentHeadward == headward_right)
{
runSprite->setFlipX(false);
}
CCSize runSize = runSprite->getContentSize();
runSpriteBatch = CCSpriteBatchNode::create("player/pao.png");
runSpriteBatch->addChild(runSprite);
addChild(runSpriteBatch);
CCArray *animatFrames = CCArray::createWithCapacity(7);
char str[100] = {0};
for(int i = 1;i < 7;++i)
{
sprintf(str, "run_%02d",i);
CCSpriteFrame *frame = runFrameCache->spriteFrameByName(str);
animatFrames->addObject(frame);
}
CCAnimation *animation = CCAnimation::createWithSpriteFrames(animatFrames,0.08f);
runSprite->runAction(CCRepeatForever::create(CCAnimate::create(animation)));
currentActionState = state_run;
}
void Player::stopCurrentAction()
{
if(currentActionState == state_idle)
{
idleSprite->stopAllActions();
idleSpriteBatch->removeChild(idleSprite,true);
removeChild(idleSpriteBatch,true);
}
else if(currentActionState == state_run)
{
runSprite->stopAllActions();
runSpriteBatch->removeChild(runSprite, true);
removeChild(runSprite,true);
}
else
{
}
}
void Player::runToCallBack()
{
this->playAction(state_idle);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>frames</key>
<dict>
<key>run_01</key>
<dict>
<key>spriteSize</key>
<string>{151, 127}</string>
<key>textureRect</key>
<string>{{0, 0}, {151,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_01</string>
</array>
</dict>
<key>run_02</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{151, 0}, {302,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_02</string>
</array>
</dict>
<key>run_03</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{302, 0}, {453,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_03</string>
</array>
</dict>
<key>run_04</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{453, 0}, {604,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_04</string>
</array>
</dict>
<key>run_05</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{604, 0}, {755,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_05</string>
</array>
</dict>
<key>run_06</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{755, 0}, {906,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_06</string>
</array>
</dict>
<key>run_07</key>
<dict>
<key>spriteSize</key>
<string>{151,127}</string>
<key>textureRect</key>
<string>{{906, 0}, {1057,127}}</string>
<key>spriteOffset</key>
<string>{-13, 42}</string>
<key>textureRotated</key>
<false/>
<key>spriteSourceSize</key>
<string>{151,127}</string>
<key>aliases</key>
<array>
<string>run_07</string>
</array>
</dict>
</dict>
<key>metadata</key>
<dict>
<key>format</key>
<integer>3</integer>
<key>size</key>
<string>{1057, 127}</string>
</dict>
</dict>
</plist>