该课程设计的主要任务是运用在《C++面向对象程序设计》课程和软件工程中学到的理论知识来指导实践,使学生了解面向对象和面向过程之间的区别,学会用类的方法解决实际问题。掌握类、模版等概念,掌握面向对象程序设计的基本概念和基本方法,锻炼学生熟练的应用面向对象的思想和设计方法解决实际问题的能力,深化对C++语言编程和开发工具运用的认识,提高软件项目开发实践能力和软件工程管理能力。
二、设计说明与调试分析
1、本次课程设计是在征得指导老师同意后,和同学合理完成该游戏。该游戏的玩法是,玩家控制主角,在场景中利用已经生成好的砖块往上方不断跳跃,直至跳到顶端游戏结束。
如图(主游戏场景):
2、游戏设计到的平台和游戏引擎:
编译平台:Microsoft Visual Studio 2010
游戏引擎:cocos2d-x 2.0.1(版本不是很高)
游戏物理引擎:Box2d
3、程序中涉及的类和函数
由于运用到了游戏引擎,其自带的类库,足够开发者进行游戏开发。所以本程序引用到了其类包括:
包括的类:
A)CCSence 场景类,用于生成游戏场景;
B)CCDirector 导演类,用于启动游戏中各个对象,及调用游戏中的各项参数;
C)CCSprite 精灵类,该类可实例化,场景中涉及到的各种怪物和主角;
D)CCLabelTTF 字符串类;
E)CCSize 屏幕高宽类,
F)HelloWorld 该类为游戏的主要编码类;
G)b2World box2d物理引擎中的世界类;
H)GameOverScene 游戏结束类;
I)b2Body 刚体类,可实现在世界中的物理效果(包括重力、冲量、碰撞等)
包括的函数:
系统框架给定的函数
HelloWorld::virtual bool init();//初始化函数
HelloWorld::static cocos2d::CCScene* scene();//场景显示函数
自定义HelloWorld类的成员函数:
bool HelloWorld::ccTouchBegan(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent)
点击鼠标左键按下的监听事件;
void HelloWorld::ccTouchEnded(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent)
点击鼠标左键放开的监听事件;
各对象的实例化:
CCSize winSize = CCDirector::sharedDirector()->getWinSize();//屏幕类
CCSprite* _ball = CCSprite::spriteWithFile("Target.png");
等
游戏算法分析:
对于游戏主角:
1、有跳跃和水平移动两种动作,用鼠标点击屏幕的位置和主角的当前位置做比对,如果高于主角当前位置,则实现跳跃,如果与主角水平,则实现水平移动。
2、切换主角往左或者往右跳的形象。判断点击的位置和主角当前位置,若在左边,则让精灵沿X方向对折_ball->setFlipX(true);
3、胜利判断,当跳跃至场景中设定高度后,调用字符串函数,作为游戏结束的提示。CCLabelTTF* pLabel1 = CCLabelTTF::create("YouWin" , "Arial",14);
4、场景中的砖块设置,将每一层设置两块砖块。用随机数放在砖块的水平位置。然后通过调整砖块的水平位置放在砖块重合。
(4)调试分析
调试是整个程序编写过程中十分重要也是很困难的一部分,在这个过程中用不少的时间进行程序的调试,在调试过程中遇到的相关问题如下:
一:语法错误
类对象没有实例化、或者将个别对象重定义。
一个精灵只能绑定一个刚体,最初将一个精灵绑定给多个刚体,导致运行出错
二:逻辑错误
回调函数没有写明回调时间。
整个调试过程主要是这么几个问题,其他基本没有出现错误。
HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Box2D/Box2D.h"
#include "SimpleAudioEngine.h"
class HelloWorld : public cocos2d::CCLayer
{
public:
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// there's no 'id' in cpp, so we recommand to return the exactly class pointer
static cocos2d::CCScene* scene();
//b2World *_world;
//b2Body *_body;
//CCSprite *_ball;
void tick(float df);
//触洌?摸t监à听瑈
virtual void ccTouchesEnded(cocos2d::CCSet* touches , cocos2d::CCEvent* event);
//点?击÷按恪?下?和í抬?起e监à听瑈事?件t
bool ccTouchBegan(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent);
void ccTouchEnded(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent);
// a selector callback
void menuCloseCallback(CCObject* pSender);
// implement the "static node()" method manually
LAYER_CREATE_FUNC(HelloWorld);
};
#endif
// __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp
#include "HelloWorldScene.h"
using namespace cocos2d;
using namespace CocosDenshion;
#include <String.h>
#define PTM_RATIO 32
CCSprite *_ball;
CCSprite *_ball2;
CCSprite *_pingtai;//梯琘1
CCSprite *_pingtai2;//梯琘2
CCSprite *_pingtai3;//梯琘3
CCSprite *_pingtai4;//梯琘4
CCSprite *_pingtai5;//梯琘5
CCSprite *_bg;//背?景°
b2World *_world;
b2Body *_body;
b2Body *_body2;
b2Vec2 gravity;
b2Body *_pingtaibody;//梯琘体?1
b2Body *_pingtaibody2;//梯琘体?2
b2Body *_pingtaibody3;//梯琘体?3
b2Body *_pingtaibody4;//梯琘体?4
b2Body *_pingtaibody5;//梯琘体?5
//#define PINGTAISHU 100;
int pingtaishu = 500;//平?台?数簓
CCSprite *ppingtai[500];//精?灵ⅷ?数簓
b2Body *p_pingtaibody[500];//梯琘子哩?刚?体?body
b2BodyDef ppingtaiBodyDef[500];//梯琘子哩?刚?体?结á构1体?
int n;//作痢?为a交?换?数簓
float length = 25.0f;
CCLabelTTF* pLabel[500];//高?度è
CCScene* HelloWorld::scene()
{
CCScene * scene = NULL;
do
{
// 'scene' is an autorelease object
scene = CCScene::create();
CC_BREAK_IF(! scene);
// 'layer' is an autorelease object
HelloWorld *layer = HelloWorld::create();
CC_BREAK_IF(! layer);
// add layer as a child to scene
scene->addChild(layer);
} while (0);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
bool bRet = false;
do
{
//
// super init first
//
CC_BREAK_IF(! CCLayer::init());
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
_ball = CCSprite::spriteWithFile("Target.png");
//_ball2 = CCSprite::spriteWithFile("xianshikuang_bg.png");
_pingtai = CCSprite::spriteWithFile("xianshikuang_bg.png");
_pingtai2 = CCSprite::spriteWithFile("xianshikuang_bg.png");
_pingtai3 = CCSprite::spriteWithFile("xianshikuang_bg.png");
_pingtai4 = CCSprite::spriteWithFile("xianshikuang_bg.png");
_pingtai5 = CCSprite::spriteWithFile("xianshikuang_bg.png");
//_pingtai->setPosition(ccp(200,100));
//添?加ó背?景°图?层?
CCRect bg_r(0,0,winSize.width,winSize.height*300);
_bg = CCSprite::spriteWithFile("bg_2.png",bg_r);
ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT,GL_REPEAT};
_bg->getTexture()->setTexParameters(&tp);
_bg->setPosition(ccp(winSize.width/2, winSize.height/2));
this->addChild(_bg,0);// 添?加ósprite节ú点?到?layer
_bg->setPosition(ccp(winSize.width/2, winSize.height/2));
_ball->setPosition(ccp(200, 100));
this->addChild(_ball,1);
this->addChild(_pingtai,1);
this->addChild(_pingtai2,1);
this->addChild(_pingtai3,1);
this->addChild(_pingtai4,1);
this->addChild(_pingtai5,1);
//this->addChild(_bg,0);
//添?加ó声Θ?音?
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(
"Tuna Melt.mp3", true);
//创洹?建¨世酣?界?的?重?力方?向ò
//b2Vec2 gravity;
gravity.Set(0.0f, -30.0f);
//b2Vec2 gravity = b2Vec2(0.0f, -30.0f);
//设Θ?置?世酣?界?里?面?的?对?象ó是?否?可é以?休Y眠?
bool doSleep = true;
_world = new b2World(gravity);
_world->SetAllowSleeping(doSleep);
_world->SetContinuousPhysics(true);
//创洹?建¨世酣?界?
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0,0);
b2Body *groundBody = _world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
b2FixtureDef boxShapeDef;
boxShapeDef.shape = &groundBox;
//底獭?
groundBox.Set(b2Vec2(0,0), b2Vec2(winSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
//左哩?侧à
groundBox.Set(b2Vec2(0,0), b2Vec2(0, winSize.height/PTM_RATIO*100));
groundBody->CreateFixture(&boxShapeDef);
//顶¥部?
groundBox.Set(b2Vec2(0, winSize.height/PTM_RATIO*80), b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO*80));
groundBody->CreateFixture(&boxShapeDef);
//右?侧à
groundBox.Set(b2Vec2(winSize.width/PTM_RATIO, winSize.height/PTM_RATIO*100), b2Vec2(winSize.width/PTM_RATIO, 0));
groundBody->CreateFixture(&boxShapeDef);
//创洹?建¨弹獭?球ò
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(100/PTM_RATIO, 100/PTM_RATIO);
//ballBodyDef.linearVelocity;
//设Θ?置?阻哩?尼á
//ballBodyDef.linearDamping = 0.1f;
//ballBodyDef.angularDamping = 0.1f;
//质ê量?设Θ?置?
//ballBodyDef.m
ballBodyDef.userData = _ball;
ballBodyDef.fixedRotation = true;
_body = _world->CreateBody(&ballBodyDef);
//圆2形?
//b2CircleShape circle;
//多à边?形?
b2PolygonShape armBox;
//circle.m_radius = 26.0/PTM_RATIO;
b2FixtureDef ballShapeDef;
//ballShapeDef.shape = &circle;
ballShapeDef.shape = &armBox;
ballShapeDef.density = 1.0f;
ballShapeDef.friction = 0.2f;
ballShapeDef.restitution = 0.1f;
armBox.SetAsBox(26.0/PTM_RATIO, 26.0/PTM_RATIO);
_body->CreateFixture(&ballShapeDef);
//设Θ?定¨刚?体?形?状痢?属?性?
//设Θ?定¨形?状痢?
b2PolygonShape pingtaiBox;
b2FixtureDef pingtaiShapeDef;
pingtaiShapeDef.shape = &pingtaiBox;
pingtaiShapeDef.density = 0.0f;
pingtaiShapeDef.restitution = 0.3f;
pingtaiShapeDef.friction = 0.5f;
pingtaiBox.SetAsBox(50/PTM_RATIO, 10/PTM_RATIO);
//
/*
//静2态?台?阶×1
b2BodyDef pingtaiBodyDef;
pingtaiBodyDef.position.Set(150/PTM_RATIO, 100/PTM_RATIO);
//pingtaiBodyDef.type = b2_dynamicBody;
pingtaiBodyDef.userData = _pingtai;
//pingtaiBodyDef.linearDamping = 1;
//pingtaiBodyDef.angularDamping = 1;
//_pingtaibody = _world->CreateBody(&pingtaiBodyDef);
_pingtaibody = _world->CreateBody(&pingtaiBodyDef);
//设Θ?定¨形?状痢?
b2PolygonShape pingtaiBox;
b2FixtureDef pingtaiShapeDef;
pingtaiShapeDef.shape = &pingtaiBox;
pingtaiShapeDef.density = 0.0f;
pingtaiShapeDef.restitution = 0.3f;
pingtaiShapeDef.friction = 0.5f;
pingtaiBox.SetAsBox(50/PTM_RATIO, 10/PTM_RATIO);
_pingtaibody->CreateFixture(&pingtaiShapeDef);
//
//静2态?阶×梯琘2
b2BodyDef pingtaiBodyDef2;
pingtaiBodyDef2.position.Set(350/PTM_RATIO, 180/PTM_RATIO);
pingtaiBodyDef2.userData = _pingtai2;
_pingtaibody2 = _world->CreateBody(&pingtaiBodyDef2);
_pingtaibody2->CreateFixture(&pingtaiShapeDef);
//
//
*/
//遍括?历え?台?阶×精?灵ⅷ?和í刚?体?
for(int i=0 ; i<pingtaishu ; i++)
{
ppingtai[i] = CCSprite::spriteWithFile("xianshikuang_bg.png");
this->addChild(ppingtai[i],1);
}
for(int j=0 ; j<pingtaishu ; j++)
{
int k = rand()%500;
int h = (j+2)/2;
//int h = rand()%50;
if(k-n>0&&k-n<100)
{
k = n+80;
}else if(n-k>0&&n-k<100)
{
k=n-130;
}
ppingtaiBodyDef[j].position.Set(k/PTM_RATIO, 100*h/PTM_RATIO);
ppingtaiBodyDef[j].userData = ppingtai[j];
p_pingtaibody[j] = _world->CreateBody(&ppingtaiBodyDef[j]);
p_pingtaibody[j]->CreateFixture(&pingtaiShapeDef);
n = k;
}
//显?示?到?达?米×数簓
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this , 0 , false);
//this->setTouchEnabled(true);
this->schedule(schedule_selector(HelloWorld::tick));
bRet = true;
} while (0);
return bRet;
}
void HelloWorld::tick(float df)
{
//cocos2d::ccTime dt;
//cocos2d: de;
_world->Step(df, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *ballData = (CCSprite *)b->GetUserData();
ballData->setPosition(ccp(b->GetPosition().x * PTM_RATIO,b->GetPosition().y * PTM_RATIO));
ballData->setRotation( -1 * CC_RADIANS_TO_DEGREES(b->GetAngle()));
//CCSprite *pingtaiData = (CCSprite *)b->GetUserData();
//pingtaiData->setPosition(ccp(b->GetPosition().x * PTM_RATIO,b->GetPosition().y * PTM_RATIO));
if (_body && _body2 == NULL)
{
b2Vec2 position = _body->GetPosition();
//b2Vec2 bgposition = _bg->getPosition();
CCPoint myPosition = this->getPosition();
CCSize screenSize = CCDirector::sharedDirector()->getWinSize(); // Move the camera.
//if (position.x > screenSize.width / 2.0f / PTM_RATIO)
if (position.y > screenSize.height / 2.0f / PTM_RATIO)
{
myPosition.y = -MIN(screenSize.height * 100.0f - screenSize.height, position.y * PTM_RATIO - screenSize.height / 2.0f);
this->setPosition(myPosition);
}
}
}
}
}
//定¨义?监à听瑈事?件t
void HelloWorld::ccTouchesEnded(CCSet* touches, CCEvent* event )
{
//
CCTouch* touch = (CCTouch*)( touches->anyObject() );
CCPoint location = touch->locationInView();
location = CCDirector::sharedDirector()->convertToGL(location);
//设Θ?置?弹獭?的?初?始?位?置?
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
CCSprite *projectile = CCSprite::create("untitled.png");
CCSize size = CCDirector::sharedDirector()->getWinSize();
//projectile->setPosition(ccp(projectile->getContentSize().width/2,size.height/2) );
projectile->setPosition(ccp(location.x,location.y) );
//点?击÷跳?跃?事?件t
this->addChild(projectile);
CCJumpTo* mJumpTo = CCJumpTo::actionWithDuration(0.2f, location, 30.0f, 1);
projectile->runAction(mJumpTo);
}
bool HelloWorld::ccTouchBegan(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent)
{
return 1;
}
void HelloWorld::ccTouchEnded(cocos2d::CCTouch *pTouch , cocos2d::CCEvent *pEvent)
{
CCPoint point = convertTouchToNodeSpace(pTouch);
CCJumpBy* mJumpTo = CCJumpTo::actionWithDuration(0.4f, point, 40.0f, 1);
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect(
"pew-pew-lei.wav");
//判D断?移?动ˉ方?向ò的?逻?辑-
//if(fabs(point.x-_ball->getPosition().x)>=fabs(point.y-_ball->getPosition().y))//则ò先è沿?x轴á移?动ˉ向ò左哩?移?动ˉ
//{
if(fabs(point.y-_ball->getPosition().y)<=length&&point.x>=_ball->getPositionX())
{
_ball->setFlipX(true);
//水?平?右?移?动ˉ
float xNexuiz = _body->GetLinearVelocity().x;
xNexuiz = 11.0f - xNexuiz;
b2Vec2 impulse = b2Vec2(xNexuiz, 0.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
}
else
//如?果?点?击÷x的?坐?标括?大洙?于瞤layer的?坐?标括?
if(fabs(point.y-_ball->getPosition().y)>length&&point.x>=_ball->getPositionX())
{
//右?跳?跃?
_ball->setFlipX(true);
float xNexuiz = _body->GetLinearVelocity().x;
xNexuiz = 16.0f - xNexuiz;
b2Vec2 impulse = b2Vec2(xNexuiz, 0.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
//jumpe
float yNexuiz = _body->GetLinearVelocity().y;
//float yNexuiz = 200;
int iJump = 1;
if(iJump < 2)
{
if(iJump == 1)
{
}
b2Vec2 impulse = b2Vec2(0.0f, 47.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
if(yNexuiz > 14)
{
b2Vec2 impulse = b2Vec2(0.0f, 44.0f - yNexuiz);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
}
iJump ++;
}
}
else if(fabs(point.y-_ball->getPosition().y)<=length&&point.x<_ball->getPositionX())//向ò左哩?移?动ˉ
{
_ball->setFlipX(false);
float xNexuiz = _body->GetLinearVelocity().x;
xNexuiz = -11.0f - xNexuiz;
b2Vec2 impulse = b2Vec2(xNexuiz, 0.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
}
else if(fabs(point.y-_ball->getPosition().y)>length&&point.x<_ball->getPositionX())//向ò左哩?跳?
{
//CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage("HelloWorld.png");
_ball->setFlipX(false);
//iDirection = DIRECTION_LEFT;
float xNexuiz = _body->GetLinearVelocity().x;
xNexuiz = -16.0f - xNexuiz;
b2Vec2 impulse = b2Vec2(xNexuiz, 0.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
//jumpe
float yNexuiz = _body->GetLinearVelocity().y;
int iJump = 1;
if(iJump < 2)
{
if(iJump == 1)
{
}
b2Vec2 impulse = b2Vec2(0.0f, 47.0f);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
if(yNexuiz > 14)
{
b2Vec2 impulse = b2Vec2(0.0f, 44.0f - yNexuiz);
_body->ApplyLinearImpulse(impulse, _body->GetWorldCenter());
}
iJump ++;
}
}
//}
}
void HelloWorld::menuCloseCallback(CCObject* pSender)
{
// "close" menu item clicked
CCDirector::sharedDirector()->end();
}
四、测试结果
1、主页面说明
如图1-1所示
2、当达到最高点之后,判断游戏结束。出现GameOver画面
五、设计心得
本着课程设计的初衷,以提高对C++及面向对象知识的运用能力,和锻炼开发及熟练掌握各类工具为目标。认认真真开展了本次课程设计。本次课程设计,本学生完成了游戏主角,主场景的设计。强调用户体验,增加了背景音乐、点击声音、图片美化。学习使用到了当今主流2d游戏开发引擎cocos2d-x和主流物理引擎box2d。该开发引擎可实现游戏的跨平台开发,在windows平台上开发成功后,可以编译到android、ios、windows phone平台使用。但由于时间不足,导致游戏偏离了最初设计。但是还是保证了游戏的完整性。我希望在接下来的时间不断完善,提高,争取将其优化为一款很火的游戏。