轨迹同步与坐标系转换
来源:互联网 发布:iphone移动数据慢 编辑:程序博客网 时间:2024/05/17 04:35
把用LUA实现游戏逻辑的重型框架改为用C++实现游戏逻辑的轻型框架。
20130528开始在2.0-x-2.03_branch_231_NoScript轻型框架中用C++写游戏逻辑。
20130524鱼轨迹同步功能的客户端动作类代码修改。
20130525鱼轨迹同步功能的服务器端代码修改。
20130527基于现有捕鱼代码中椅子ID<->视图ID之间的转换公式,给出收发坐标数据时,服务器端坐标<->客户端本地坐标的转换公式。
20130528在无Lua工程中测试鱼轨迹和鱼轨迹阵,测试用轨迹编辑器生成的轨迹数据生成三阶贝塞尔曲线轨迹。
【网狐捕鱼的轨迹同步:有服务器轨迹建立时间但没有根据客户端收到轨迹包的时间差建立同步的轨迹,服务器有简单的轨迹管理(有轨迹建立和轨迹销毁,无轨迹更新),后进桌子的玩家收不到服务器端先前下发的轨迹,客户端没有考虑建立轨迹动作对象时的动作已消逝时间InitElapsed=ClientBuildTime-ServerBuildTime。——结论:完全没有做轨迹同步】
#define FISH_ALIVE_TIME 20 //存活时间
鱼轨迹结构体有一个字段
DWORD dwBuildTime; //建立时间
//服务器建立鱼轨迹时
pServerFishTrace->dwBuildTime=::time(NULL);
//服务器遍历鱼轨迹,清除鱼轨迹
//判断时间
if (dwNowTime>=(pServerFishTrace->dwBuildTime+FISH_ALIVE_TIME))
{
DestroyFishTrace(pServerFishTrace);
continue;
}
【欢乐捕鱼的轨迹同步:轨迹建立时间以服务器端的为准,进入桌子后,举手的玩家会收到服务器端桌子内所有存活的轨迹数据,每条轨迹服务器端发给每个客户端只发一次,客户端根据时间差InitElapsed建立同步的轨迹。】
LONGLONG lPutFishTime = GetTickCount64(); // 获得当前的ms 时间
CCActionInterval类添加了一个成员变量m_InitElapsed,在CCActionInterval、CCMoveTo、CCMoveBy、CCSequence、CCActionEase、CircleAction、myAction、CCBezierBy、CCBezierTo等动作类调用CCActionInterval::initWithDuration函数的地方添加了一个控制参数InitElapsed,其他动作类暂未添加。
Cocos2d-x需要修改的地方:
修改1:CCAction.h/CCFiniteTimeAction类增加一个虚函数
//获取延时动作对象创建时,该延时动作已消逝的时间,add by Ivan_han 20130524
virtual float getInitElapsed(void) { return 0; }
修改2:CCActionInterval.h/.cpp/CCActionInterval类增加一个成员变量和成员函数,initWithDuration、actionWithDuration、create这三个函数加上一个参数float InitElapsed=0
//获取延时动作对象创建时,该延时动作已消逝的时间,add by Ivan_han 20130524
inline float getInitElapsed(void) { return m_InitElapsed; }
float m_InitElapsed;//动作已经消逝时间,add by Ivan_han 20130524
bool initWithDuration(float d,float InitElapsed=0);
CC_DEPRECATED_ATTRIBUTE static CCActionInterval* actionWithDuration(float d,float InitElapsed=0);
static CCActionInterval* create(float d,float InitElapsed=0);
cpp文件的改动:
CCActionInterval* CCActionInterval::actionWithDuration(float d,float InitElapsed/*=0*/)
return CCActionInterval::create(d,InitElapsed);
CCActionInterval* CCActionInterval::create(float d,float InitElapsed/*=0*/)
CCActionInterval *pAction = new CCActionInterval();
pAction->initWithDuration(d,InitElapsed);
pAction->autorelease();
bool CCActionInterval::initWithDuration(float d,float InitElapsed/*=0*/)
m_fDuration = d;
m_InitElapsed=InitElapsed;//add by Ivan_han 20130524
// prevent division by 0
// This comparison could be in step:, but it might decrease the performance
// by 3% in heavy based action games.
if (m_fDuration == 0)
{
m_fDuration = FLT_EPSILON;
}
m_bFirstTick = true;
return true;
CCObject* CCActionInterval::copyWithZone(CCZone *pZone)
CCZone* pNewZone = NULL;
CCActionInterval* pCopy = NULL;
if(pZone && pZone->m_pCopyObject)
{
//in case of being called at sub class
pCopy = (CCActionInterval*)(pZone->m_pCopyObject);
}
else
{
pCopy = new CCActionInterval();
pZone = pNewZone = new CCZone(pCopy);
}
CCFiniteTimeAction::copyWithZone(pZone);
CC_SAFE_DELETE(pNewZone);
pCopy->initWithDuration(m_fDuration,m_InitElapsed);
return pCopy;
}
void CCActionInterval::step(float dt)
if (m_bFirstTick)
{
m_bFirstTick = false;
m_elapsed = MAX (0, MIN(m_InitElapsed,m_fDuration));//add by Ivan_han 20130524
}
else
m_elapsed += dt;
}
this->update(MAX (0,// needed for rewind. elapsed could be negative
MIN(1, m_elapsed /
MAX(m_fDuration, FLT_EPSILON)// division by 0
));
修改3:CCSequence::initWithTwoActions
CCActionInterval::initWithDuration(d);
改为:
//modify by Ivan_han 20130524
CCActionInterval::initWithDuration(d,pActionOne->getInitElapsed());
if (CCActionInterval::initWithDuration(pAction->getDuration()))
改为:
if (CCActionInterval::initWithDuration(pAction->getDuration(),pAction->getInitElapsed()))
修改5:CCActionInterval.h/.cpp/CCMoveTo、CCMoveBy、CCBezierBy、CCBezierTo
20130529在无Lua工程中添加轨迹同步的客户端代码。CCActionInterval类添加了一个成员变量m_InitElapsed,在CCActionInterval、CCMoveTo、CCMoveBy、CCSequence、CCActionEase、myAction、CCBezierBy、CCBezierTo等动作类调用CCActionInterval::initWithDuration函数的地方添加了一个控制参数InitElapsed,其他动作类暂未添加。
20130529实现SP<->CP的坐标互转。
//add by Ivan_han 20130529
static float s_a;//SP->CP需要经过的逆时针旋转弧度值,负值表示顺时针旋转弧度值
static float s_px;//SP->CP需要经过的原点位移的x值
static float s_py;//SP->CP需要经过的原点位移的y值
//add by Ivan_han 20130529
float CShip::s_a=0;//SP->CP需要经过的逆时针旋转弧度值,负值表示顺时针旋转弧度值
float CShip::s_px=0;//SP->CP需要经过的原点位移的x值
float CShip::s_py=0;//SP->CP需要经过的原点位移的y值
//获取客户端的坐标变换参数a和(px,py)
float CShip::getAntiClockwiseRotation(int MyRoomSeatID/*玩家自己的逻辑ID或椅子号*/, int MyGameSeatID/*玩家自己的视图ID*/,float *px,float *py)
{
//视图ID=右下角
if(MyGameSeatID==3)
{
//逻辑ID=左上角
if(MyRoomSeatID==0)
{
*px=MAX_WIDTH;
*py=MAX_HEIGHT;
return -M_PI;
}
//逻辑ID=右上角
if(MyRoomSeatID==1)
{
*px=0;
*py=MAX_WIDTH;
return -M_PI*0.5;
}
//逻辑ID=左下角
if(MyRoomSeatID==2)
{
*x=MAX_HEIGHT;
*y=0;
return -M_PI*1.5;
}
//逻辑ID=右下角
if(MyRoomSeatID==3)
{
*x=0;
*y=0;
return 0;
}
}
//视图ID=左下角
if(MyGameSeatID==4)
{
//逻辑ID=左上角
if(MyRoomSeatID==0)
{
*x=MAX_HEIGHT;
*y=0;
return -M_PI*1.5;
}
//逻辑ID=右上角
if(MyRoomSeatID==1)
{
*x=MAX_WIDTH;
*y=MAX_HEIGHT;
return -M_PI;
}
//逻辑ID=左下角
if(MyRoomSeatID==2)
{
*x=0;
*y=0;
return 0;
}
//逻辑ID=右下角
if(MyRoomSeatID==3)
{
*x=0;
*y=MAX_WIDTH;
return -M_PI*0.5;
}
}
*x=0;
*y=0;
return 0;
}
static CCPoint SP2CP(const CCPoint& SP);
//发包时坐标转换
static CCPoint CP2SP(const CCPoint& CP);
{
CCPoint ret=ccp(SP.x*cos(s_a)-SP.y*sin(s_a)+s_px,SP.x*sin(s_a)+SP.y*cos(s_a)+s_py);
return ret;
}
{
CCPoint ret=ccp((CP.x-s_px)*cos(s_a)+(CP.y-s_py)*sin(s_a),-(CP.x-s_px)*sin(s_a)+(CP.y-s_py)*cos(s_a));
return ret;
}
20130529晚上修改后:
逻辑ID唯一决定视图ID({左下方=3、右下方=2}),视图ID不再从{左下方=3、右下方=2}中随机选择;服务器端矩形<->客户端矩形的坐标映射要满足内点<->内点、外点<->外点、边界点<->边界点;矩形不能旋转90°和270°,只能旋转0°和180°。
{
assert(LogicID>=0 && LogicID<=3);
if(LogicID==0)//逻辑ID=左上方
{
*a=M_PI;
*px=MAX_WIDTH;
*py=MAX_HEIGHT;
*IsNeedTransform=true;
return 2;//视图ID=右下方,即[0]=2
}
if(LogicID==1)//逻辑ID=右上方
{
*a=M_PI;
*px=MAX_WIDTH;
*py=MAX_HEIGHT;
*IsNeedTransform=true;
return 3;//视图ID=左下方,即[1]=3
}
if(LogicID==2)//逻辑ID=左下方
{
*a=0;
*px=0;
*py=0;
*IsNeedTransform=false;
return 3;//视图ID=左下方,即[2]=3
}
if(LogicID==3)//逻辑ID=右下方
{
*a=0;
*px=0;
*py=0;
*IsNeedTransform=false;
return 2;//视图ID=右下方,即[3]=2
}
return 0;
}
或
CCPoint CShip::SP2CP(const CCPoint& SP)
{
#if 0//数学上推导出的一般公式,未化简
CCPoint ret=ccp(SP.x*cos(s_a)-SP.y*sin(s_a)+s_px,SP.x*sin(s_a)+SP.y*cos(s_a)+s_py);
#else//运行效率高些而已
CCPoint ret=SP;
if(s_IsNeedTransform)
{
ret=ccp(MAX_WIDTH-SP.x,MAX_HEIGHT-SP.y);
}
#endif
return ret;
}
CCPoint CShip::CP2SP(const CCPoint& CP)
{
#if 0//数学上推导出的一般公式,未化简
CCPoint ret=ccp((CP.x-s_px)*cos(s_a)+(CP.y-s_py)*sin(s_a),-(CP.x-s_px)*sin(s_a)+(CP.y-s_py)*cos(s_a));
#else//运行效率高些而已
CCPoint ret=CP;
if(s_IsNeedTransform)
{
ret=ccp(MAX_WIDTH-CP.x,MAX_HEIGHT-CP.y);
}
#endif
return ret;
}
修改后的代码:
//add by Ivan_han 20130529
/*
逻辑ID唯一决定视图ID({左下方=3、右下方=2}),视图ID不再从{左下方=3、右下方=2}中随机选择;
服务器端矩形<->客户端矩形的坐标映射要满足内点<->内点、外点<->外点、边界点<->边界点;
矩形不能旋转90°和270°,只能旋转0°和180°。
*/
//根据逻辑ID获取客户端的视图ID以及决定是否进行坐标变换
static int getViewID(int LogicID,bool *IsNeedTransform);
//收包时坐标转换
static CCPoint SP2CP(const CCPoint& SP);
//发包时坐标转换
static CCPoint CP2SP(const CCPoint& CP);
//add by Ivan_han 20130529
static bool s_IsNeedTransform;//false:SP=CP,true:SP≠CP
//add by Ivan_han 20130529
bool CShip::s_IsNeedTransform=false;
//shipIndex = rand()%2+2;
//add by Ivan_han 20130529
shipIndex=CShip::getViewID(seatAbsoluteIndex,&CShip::s_IsNeedTransform);
//根据逻辑ID获取客户端的视图ID以及决定是否进行坐标变换
int CShip::getViewID(int LogicID,bool *IsNeedTransform)
{
assert(LogicID>=0 && LogicID<=3);
if(LogicID==0)//逻辑ID=左上方
{
*IsNeedTransform=true;
return 2;//视图ID=右下方,即[0]=2
}
if(LogicID==1)//逻辑ID=右上方
{
*IsNeedTransform=true;
return 3;//视图ID=左下方,即[1]=3
}
if(LogicID==2)//逻辑ID=左下方
{
*IsNeedTransform=false;
return 3;//视图ID=左下方,即[2]=3
}
if(LogicID==3)//逻辑ID=右下方
{
*IsNeedTransform=false;
return 2;//视图ID=右下方,即[3]=2
}
return 0;
}
CCPoint CShip::SP2CP(const CCPoint& SP)
{
CCPoint ret=SP;
if(s_IsNeedTransform)
{
ret=ccp(MAX_WIDTH-SP.x,MAX_HEIGHT-SP.y);
}
return ret;
}
CCPoint CShip::CP2SP(const CCPoint& CP)
{
CCPoint ret=CP;
if(s_IsNeedTransform)
{
ret=ccp(MAX_WIDTH-CP.x,MAX_HEIGHT-CP.y);
}
return ret;
}
//获取别人在自己客户端中的视图ID
int CShipMgr::getViewID(int CurLogicID, int MyLogicID, int MyViewID)
{
// 4 个位置
// 逻辑ID左上角顺时针方向顺序 0132
// 视图ID左上角顺时针方向顺序 0123
//逻辑ID都在上面
if (CurLogicID+MyLogicID==1)
return 5-MyViewID;
//逻辑ID都在下面
if (CurLogicID+MyLogicID==5)
return 5-MyViewID;
//逻辑ID一上一下,且正对面
if (CurLogicID+MyLogicID==2||CurLogicID+MyLogicID==4)
return 3-MyViewID;
//逻辑ID一上一下,且斜对面
if (CurLogicID+MyLogicID==3)
{
//自己的逻辑ID在下面
if(MyLogicID>1)
return CurLogicID;
//自己的逻辑ID在上面
if(MyLogicID<2)
return MyViewID-2;
}
return -1;//错误值
}
void SIX_Proxy::OnAddOneFish(Rsp_AddOneFish *pReal,float InitElapsed)
{
CCPoint startCP=m_pShipMgr->SP2CP(CCP(pReal->startP));//代表点
//FishPathType=1,代表方向
CCPoint arg1CP=ccpSub(m_pShipMgr->SP2CP(CCP(pReal->arg1)),m_pShipMgr->SP2CP(ccp(0,0)));//代表方向,≠CCP(pReal->arg1),≠m_pShipMgr->SP2CP(CCP(pReal->arg1))
if(pReal->FishPathType==2||pReal->FishPathType==5)
arg1CP=m_pShipMgr->SP2CP(CCP(pReal->arg1));//代表点
if(pReal->FishPathType==3)
arg1CP=CCP(pReal->arg1);//既不代表点,也不代表方向,仅用于存储
//FishPathType=5,代表点
CCPoint arg2CP=m_pShipMgr->SP2CP(CCP(pReal->arg2));
if(pReal->FishPathType==3)
{
arg2CP=ccp(pReal->arg2.x,m_pShipMgr->SD2CD(pReal->arg2.y));//y分量代表方向
}
//FishPathType=5,代表点
CCPoint arg3CP=m_pShipMgr->SP2CP(CCP(pReal->arg3));
FishManage::GetInstance()->addOneFish(pReal->FishKind,pReal->FishPathType,pReal->FishMoveSecs,InitElapsed,startCP,arg1CP,arg2CP,arg3CP);
}
20130611-20130612服务器部署到非本机后时间同步问题修改:
增加一条S->C的网络通讯协议:
/// 初始游戏
struct INIT_GAME : public DATA_BASE<INIT_GAME>
{
INIT_GAME()
{
iScenesID = 0;
tServerTime = 0;
}
int iScenesID; ///< 场景ID
unsigned long long tServerTime; ///< 服务器开机的日历时刻(常量SK)
};
服务器端:
// 举手
int CFishTable::OnRaiseHand(LONGLONG lChannelNo)
{
printf("举手 ,可以发鱼啦! \n");
//LONGLONG lPutFishTime = GetTickCount64(); // 获得当前的 ms 时间,即发鱼的基础时间
// 设置场景
if (SCENES_MIN <= m_stTableInfo.iScenesID && SCENES_MAX >= m_stTableInfo.iScenesID)
{
// 当前时间 - 已经运行时间 = 服务器开机日历时刻(常量SK)
ULONGLONG ullRunTime = GetTickCount64();
ULONGLONG tStartComputerTime = CEasyDataTime::get_current_time_diff_millitm_from_1970() - ullRunTime;//SK
INIT_GAME stInitGame;
stInitGame.iScenesID = m_stTableInfo.iScenesID;
stInitGame.tServerTime = tStartComputerTime;
send_game_data(lChannelNo, TABLE_MAIN, TABLE_SUB_INIT_GAME, (BYTE*)&stInitGame, sizeof(INIT_GAME));
}
m_stFishTraceMgr.OnRaiseHand(lChannelNo);
// 子弹发给刚举手的玩家
m_stBulletMgr.ShootAllBullet( lChannelNo );
return 0;
}
客户端:
SIX_Proxy类加一个成员变量:
long long m_tClientServerTimeInterval;//△t=CK-SK,可正可负可零
//客户端收到网络包计算时间差时不直接调用::GetTickCount64(),add by Ivan_han 20130614
static unsigned long long GetTickCount64();
unsigned long long SIX_Proxy::GetTickCount64()
{
//#ifdef WIN32
#if 0
return ::GetTickCount64();
#else
//此时假定客户端的开机时刻是1970年
return CEasyDataTime::get_current_time_diff_millitm_from_1970();
#endif
}
SIX_Proxy::HandlePacketTable成员函数:
case TABLE_SUB_INIT_GAME:
{
INIT_GAME *pInit = (INIT_GAME *)pData;
if (NULL == pInit && SCENES_MIN > pInit->iScenesID && SCENES_MAX < pInit->iScenesID)
break;
if(NULL == pSceneGame)
break;
// 当前时间 - 已经运行时间 = 客户端开机的日历时刻(常量CK)
ULONGLONG ullRunTime = SIX_Proxy::GetTickCount64();
ULONGLONG ullRunTime1 = CEasyDataTime::get_current_time_diff_millitm_from_1970();
ULONGLONG tStartComputerTime =ullRunTime1 - ullRunTime;//CK
/*
add by Ivan_han 20130612
m_tClientServerTimeInterval=△t=CK-SK表示在同一真实时刻,客户端调用GetTickCount64()的值与服务器端调用GetTickCount64()的值的时间差,
当服务器与客户端在同一台机器上时,△t=0。
虽然△t不一定就等于客户端开机的真实时刻与服务器端开机的真实时刻的时间差,
因为客户端、服务器端与真实时间可能不一致,
但是不管客户端、服务器端是否与真实时间是否一致,△t都是有用的。
一般来说(假定客户端、服务器端与真实时间都一致的情况),△t>0<=>CK>SK表示服务器端先开机。
20130627
unsigned long long tCurrentServerTime; ///< 服务器发包的日历时刻(变量ST),注意CEasyDataTime::get_current_time_diff_millitm_from_1970()返回的不是当前真实时刻至1970年1月1日的毫秒差值
时间同步代码修改,增加了一个tCurrentServerTime字段(变量ST),dT=ST-CT+ε,dT表示同一真实时刻,服务器端调用CEasyDataTime::get_current_time_diff_millitm_from_1970()与客户端调用CEasyDataTime::get_current_time_diff_millitm_from_1970()的时间差。
目前时间同步存在误差ε,ε是客户端收到时间同步信息的包延迟。理论上,同一局域网的不同客户端的包延迟ε是一样的,看到的鱼轨迹也是一样的。
20130628
计算收包延迟ε的近似值,完善时间同步功能。理论上,任意客户端看到的鱼轨迹是一样的;
记发包延迟为ε',服务器处理举手请求时间为△S≈0,收包延迟为ε≈ε',时间同步信息到达时刻为c2,则收包延迟ε≈(c2-c1)/2
*/
m_tClientServerTimeInterval = (tStartComputerTime - pInit->tServerTime);//△t=CK-SK
pSceneGame->changeScenes( pInit->iScenesID );
break;
}
case RSP_ADD_ONE_FISH:
{
if(NULL != pSceneGame && !pSceneGame->IsInited())
return;
Rsp_AddOneFish *pReal = (Rsp_AddOneFish*)pData;
TBuildTraceTime ClientBuildTime=SIX_Proxy::::GetTickCount64();
long long llInitElapsed=ClientBuildTime-pReal->BuildTraceTime+m_tClientServerTimeInterval;
float InitElapsed =llInitElapsed * 0.001;
OnAddOneFish(pReal,InitElapsed);
break;
}
case RSP_ADD_ONE_FISH_ARR:
{
if(NULL != pSceneGame && !pSceneGame->IsInited())
return;
Rsp_AddOneFish_Arr *pReal = (Rsp_AddOneFish_Arr*)pData;
TBuildTraceTime ClientBuildTime=SIX_Proxy::::GetTickCount64();
for(int i=0;i<pReal->nFishCount;i++)
{
long long llInitElapsed=ClientBuildTime-pReal->Arr[i].BuildTraceTime+m_tClientServerTimeInterval;
float InitElapsed =llInitElapsed * 0.001;
OnAddOneFish(&pReal->Arr[i],InitElapsed);
}
break;
}
case RSP_ADD_ONE_FISH_LIST:
{
if(NULL != pSceneGame && !pSceneGame->IsInited())
return;
Rsp_AddOneFish_List *pReal = (Rsp_AddOneFish_List*)pData;
TBuildTraceTime ClientBuildTime=SIX_Proxy::::GetTickCount64();
for(int i=0;i<pReal->nFishCount;i++)
{
long long llInitElapsed=ClientBuildTime-(*pReal)[i].BuildTraceTime+m_tClientServerTimeInterval;
float InitElapsed =llInitElapsed * 0.001;
OnAddOneFish(&(*pReal)[i],InitElapsed);
}
break;
}
修改前:
CCPoint CShipMgr::SP2CP(const CCPoint& SP)
{
CCPoint ret=SP;
if(mIsNeedTransform)
{
ret=ccp(MAX_WIDTH-SP.x,MAX_HEIGHT-SP.y);
}
return ret;
}
{
if(mIsNeedTransform)
{
ret=ccp(MAX_WIDTH-CP.x,MAX_HEIGHT-CP.y);
}
return ret;
}
{
CCPoint ret=SD;
if(mIsNeedTransform)
{
ret=ccp(-SD.x,-SD.y);
}
return ret;
}
{
return SD2CD(CD);
}
{
float ret=SD;
if(mIsNeedTransform)
{
ret=SD+M_PI;
}
return ret;
}
{
return SD2CD(CD);
}
修改后:
CCPoint CShipMgr::SP2CP(const CCPoint& SP)
{
CCPoint ret=CShipMgr::ScaleTrans(1280,733,MAX_WIDTH,MAX_HEIGHT,SP);
if(mIsNeedTransform)
{
ret=ccp(MAX_WIDTH-ret.x,MAX_HEIGHT-ret.y);
}
return ret;
}
{
if(mIsNeedTransform)
{
ret=ccp(1280-ret.x,733-ret.y);
}
return ret;
}
{
//CCPoint ret=SD;
//if(mIsNeedTransform)
//{
// ret=ccp(-SD.x,-SD.y);
//}
CCPoint ret=ccpSub(SP2CP(SD),SP2CP(ccp(0,0)));
return ret;
}
{
//return SD2CD(CD);
CCPoint ret=ccpSub(CP2SP(CD),CP2SP(ccp(0,0)));
return ret;
}
{
//float ret=SD;
//if(mIsNeedTransform)
//{
// ret=SD+M_PI;
//}
//return ret;
CCPoint cd=SD2CD(ccp(cos(SD),sin(SD)));
float ret=atan2(cd.y,cd.x);
return ret;
}
{
//return SD2CD(CD);
CCPoint sd=CD2SD(ccp(cos(CD),sin(CD)));
float ret=atan2(sd.y,sd.x);
return ret;
}
float CShipMgr::ScaleXTrans(int W,int W1,float x)
{
return x*W1/W;
}
float CShipMgr::ScaleXTrans(float ScaleX,float x)
{
return x*ScaleX;
}
CCPoint CShipMgr::ScaleTrans(int W,int H,int W1,int H1,const CCPoint& p)
{
CCPoint ret=CCPoint(ScaleXTrans(W,W1,p.x),ScaleXTrans(H,H1,p.y));
return ret;
}
CCPoint CShipMgr::ScaleTrans(float ScaleX,float ScaleY,const CCPoint& p)
{
CCPoint ret=CCPoint(ScaleXTrans(ScaleX,p.x),ScaleXTrans(ScaleY,p.y));
return ret;
}
- 轨迹同步与坐标系转换
- 球面坐标系与直角坐标系转换
- 三维坐标系介绍与转换
- 记录:成像与坐标系转换
- 关于坐标系转换与点在坐标系之间的转换
- 直角坐标系与极坐标系了解与转换
- 3dsMax与Unity3D坐标系同步
- 3dsMax与Unity3D坐标系同步
- 地方坐标系与国家坐标系转换方法探讨
- 关于84坐标系与54坐标系转换问题
- GCJ-02 坐标系与百度坐标系的转换算法
- 数码相片框标坐标系与扫描坐标系转换
- 世界坐标系与像素坐标系的点互相转换
- 各地图API坐标系统比较与转换(WGS84坐标系、火星坐标系、百度坐标系、搜狗坐标系、图吧坐标系)
- GIS中的坐标系定义与转换
- GIS中的坐标系定义与转换
- OGRE之坐标系、向量与转换
- GPS坐标系与直角坐标的转换
- Android4.2的多用户管理中关于SD卡的实现改动
- 也谈栈和栈帧(一)
- Hadoop HDFS配置
- MYSQL创建、删除和选择数据库
- http://zhan.renren.com/buildcpp?gid=3602888497996145028&checked=true
- 轨迹同步与坐标系转换
- “高斯白噪声”之大白话
- 设置project不验证js
- hdu 3732
- 获取ANDROID唯一识别码
- 线下app推广
- 保护自己,学会对抗手机、平板等移动网络钓鱼
- python的for如何获得当前循环次数?
- 贪心算法求解哈弗曼编码