求几年前的单机塔防游戏大全概是13年玩的,飞行射击游戏来的,飞机只能在一个范围内来回飞行,跟纸飞机大战差不多

汉化版繁体759M
美版英文1.35G
汉化版简体487.4M
日版日文41.5M
日版日文84.7M
汉化版简体384.2M
汉化版简体77.6M
中文版繁体69.9M
汉化版简体701.4M
汉化版简体168.4M
日版日文186.7M
日版日文303.9M
美版英文155.6M
美版英文269.3M
欧版英文546.5M
日版日文66.4M
日版日文226.5M
日版英文54.8M从小白到高手的成长之路 (好吧,现在我还是只小白~~)
飞行射击游戏的C++实现:一次课程作业
  该程序为可视化界面下的飞行射击游戏。程序中,下方的 * 符号代表玩家的飞机,上方的 + 符号代表敌方飞机,o 代表射出的子弹,使用a键和d键控制飞机向左向右移动,使用空格键退出游戏。当子弹击中敌方飞机后,得一分,当玩家被击中后,屏幕上会显示You are hit!。
将逻辑层和显示层分开,便于更换显示样式。
逻辑层可以用于之后类似游戏的开发中。
底层使用双精度浮点数表示坐标,具有更高的精度。
定义了异常类型。
定义了模板类fsPoint&T&。
代码中有丰富的注释,代码风格良好,便于阅读。
使用chrono库处理有关计时的问题。
  该程序在设计上包括逻辑层和显示层两层,目前显示层使用控制台有关函数实现。逻辑层不依赖于现实层,从而保证了一定的扩展性。
逻辑层(fsObject.h)
  逻辑层的定义和实现在fsObjects.h及fsObjects.cpp中。下面是对逻辑层的介绍。
自定义数据类型
fsPoint Class
  存储程序中所需的点的坐标。这是一个模板类
template &typename T&
class fsPoint {
T x, // 归一化之后的坐标位置,左上角为0,0
右下角为1,1
fsPoint() {};
fsPoint(T xval, T yval) :x(xval), y(yval) {};
fsPoint&T& & operator = (const fsPoint&T& & rval);
一些枚举类型
enum bulletSource {
player = 1,
enemy = 2,
playerFriend = 3
enum Direction
right = 3,
fsInvalidInitializePointValException Class
  创建fsObject时所给出的点不满足左上点和右下点之间的坐标关系。
::logic_error {
fsInvalidInitializePointValException() ;
  如果在规定的两次开火间隔时间内执行fsAirCraft.fire()就会抛出此异常。提示需要等待足够的时间后才能执行上述方法。
:public ::logic_error {
fsInvalidFireTimeException() ;
fsInvalidBulletSpeedInException Class
  输入的子弹速度无效。
::logic_error {
fsInvalidBulletSpeedInException();
fsOutRangeMoveToPointException Class
在moveToPoint()方法中,输入的目标点超出了屏幕显示范围(也即目标点的坐标之一不在0到1之间)。
::logic_error {
fsOutRangeMoveToPointException();
游戏中使用的物体
  所有的物体,包括玩家飞机,敌方飞机和子弹,以及分数显示,都是一个类,其基类为fsObject Class
  每一个物体都通过两个点,即两个fsPoint&double&类型的变量来确定所处位置,两个点分别为左上角的点和右下角的点。程序中将所处的空间进行了归一化,所有的物体的横纵坐标都只能在0到1之间。之后再通过显示层将其绘制到屏幕上。
fsObject Class
  游戏中所用物体的基类,该类的定义如下:
class fsObject {
fsObject() {};
fsObject(const fsPoint&double& & upperleft,
const fsPoint&double& & lowerright,
const fsColor & fgc,
char sym = 0
fsPoint&double& getUpperLeftCorner const();
fsPoint&double& getLowerRightCorner const();
char getSymbol() const ;
void setSymbol(char c) ;
int getVisible() const { return this-& }
void setVisible(int newVisible) { this-&visible = newV return; }
int moveleft() ;
int moveright();
int moveVertically(double delta) ;
int moveHorizontally(double delta);
int moveToPoint(const fsPoint&double& & dest);
void printpos() const ;
fsBullet Class
  用于表示游戏中飞行的子弹,其定义及有关方法如下:
class fsBullet : public fsObject (
fsBullet() {};
fsBullet(bulletSource sourceOfBullet,
Direction directionOfBullet,
int damageOfBullet,
fsPoint&double& sourceulc,
double bulletVec = FS_DEFAULT_BULLET_FLY_SPEED
int updataPosition();
fsAircraft Class
  游戏中所有飞机的基类,玩家的飞机和敌人的飞机均由此派生。
class fsAircraft : public fsObject {
fsAircraft() {};
fsAircraft(fsPoint&double& upperleft,
fsPoint&double& lowerright,
fsColor fgc,
std::chrono::milliseconds fireinterval = FS_DEFAULT_FIRE_INTERVAL
inline int fireReady();
fsBullet fire();
int setBullteDamage(int newBulletDamage);
void setSource(bulletSource newSource);
void setFireDirection(Direction newDirection);
int getLifeLeft() const;
int gotHit(int bulletDamage = 1);
fsMyAircraft Class
  玩家的飞机,结合fsObject中的moveleft()和moveright()方法玩家可以通过键盘操纵其位置。
class fsMyAircraft : public fsAircraft {
fsMyAircraft(fsPoint&double& upperleft,
fsPoint&double& lowerright,
fsColor fgc,
std::chrono::milliseconds
fireinterval = FS_DEFAULT_FIRE_INTERVAL
fsEnemyAircraft Class
  敌人的飞机,飞行方向随机决定。
class fsEnemyAircraft : public fsAircraft {
fsEnemyAircraft(fsPoint&double& upperleft,
fsPoint&double& lowerright,
fsColor fgc,
std::chrono::milliseconds
fireinterval = FS_DEFAULT_FIRE_INTERVAL
int updatePosition();
fsScoreBoard Class
  计分版 用于记录玩家的分数
class fsScoreBoard : public fsObject {
fsScoreBoard() {};
fsScoreBoard(const fsPoint&double& & upperleft,
const fsPoint&double& & lowerright,
const fsColor & fgc = FS_DEFAULT_SCOREBOARD_COLOR
int getCurScore() const ;
void addScore(int delta = 1) ;
逻辑层中提供的一些函数
int fsOverlap(const fsObject & obj1, const fsObject & obj2);
template &typename T&
bool fsPointInRect(fsPoint&T& point, fsPoint&T& RectUpperLeft, fsPoint&T& RectLowerRight);
逻辑层中定义的一些常量
#define FS_DEFAULT_FIRE_INTERVAL std(500)
#define FS_DEFAULT_FRAME_INTERVAL 10
#define FS_DEFAULT_MOVE_STEP 0.01
#define FS_MOVE_LEFT_KEY 'a'
#define FS_MOVE_RIGHT_KEY 'd'
#define FS_ESCAPE_KEY ' '
#define FS_DEFAULT_BULLET_FLY_SPEED 3.0
#define FS_DEFAULT_AIRCRAFT_FLY_SPEED 0.1
#define FS_DEFAULT_BULLET_COLOR fsColor(255,0,0,0)
#define FS_DEFAULT_BULLET_SIZE 0.1
#define FS_DEFAULT_BULLET_SIZE_X 0.05
#define FS_DEFAULT_BULLET_SIZE_Y 0.1
#define FS_DEFAULT_PLAYER_SYMBOL '*'
#define FS_DEFAULT_ENEMY_SYMBOL '+'
#define FS_DEFAULT_BULLET_SYMBOL 'o'
#define FS_DEFAULT_SCOREBOARD_COLOR fsColor(0,255,0,0)
#define FS_DEFAULT_COORD_LOWER_LIMIT 0.05
#define FS_DEFAULT_COORD_UPPER_LIMIT 0.95
#define FS_DEFAULT_COORD_LOWER_LIMIT_X 0.05
#define FS_DEFAULT_COORD_UPPER_LIMIT_X 0.95
#define FS_DEFAULT_COORD_LOWER_LIMIT_Y 0.05
#define FS_DEFAULT_COORD_UPPER_LIMIT_Y 0.95
显示层(fsDraw.h)
  该层主要提供了将物体显示在控制台中的函数,如下:
void gotoxy(int x, int y) {
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
void setcolor(WORD color) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
int fsDraw(const fsObject & fso) {
gotoxy((fso.getUpperLeftCorner()).x * FS_DEFAULT_CONSOLE_BUFFER_SCALE,
(fso.getUpperLeftCorner()).y * FS_DEFAULT_CONSOLE_BUFFER_SCALE);
setcolor(FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED);
cout && fso.getSymbol();
int fsDrawScoreBoard(const fsScoreBoard & fsb) {
gotoxy((fsb.getUpperLeftCorner()).x * FS_DEFAULT_CONSOLE_BUFFER_SCALE,
(fsb.getUpperLeftCorner()).y * FS_DEFAULT_CONSOLE_BUFFER_SCALE);
setcolor(FOREGROUND_GREEN);
cout && "SCORE: " && fsb.getCurScore();
程序执行流程(fsTest.h)
  在包含的程序提供的类文件(fsObjects.h, fsDraw.h)后,简单的射击小游戏可以如下操作(例子请见fsTest.h中的gameTestOneBuf()函数):
(1)初始化有关物体,主要是玩家飞机和敌方飞机、计分版,以及有关的子弹
fsMyAircraft playerAircraft = fsMyAircraft(fsPoint&double&(0.5, 0.99),
fsPoint&double&(0.51, 1.00),
fsColor(255, 255, 255, 0));
fsEnemyAircraft enemy1 = fsEnemyAircraft(fsPoint&double&(0.5, 0.10),
fsPoint&double&(0.51, 0.11),
fsColor(255, 0, 0, 0));
fsScoreBoard scoreBoard = fsScoreBoard(fsPoint&double&(1.1, 0.3), fsPoint&double&(1.11, 0.31));
Sleep(1000);
if (playerAircraft.fireReady()) {
mybullet = playerAircraft.fire();
if (enemy1.fireReady()) {
enemybullet = enemy1.fire();
char ch = '/0';
(2) 进入游戏循环,在游戏循环中,需要依次完成读取按键,更新各物体状态,绘出物体,子弹击中判断等过程
while (1) {
if (_kbhit())
ch = getche();
switch (ch)
case FS_MOVE_LEFT_KEY:
playerAircraft.moveleft();
case FS_MOVE_RIGHT_KEY:
playerAircraft.moveright();
case FS_ESCAPE_KEY:
cout && "You ended this game!";
if (playerAircraft.fireReady()) {
mybullet = playerAircraft.fire();
if (enemy1.fireReady()) {
enemybullet = enemy1.fire();
mybullet.updataPosition();
enemybullet.updataPosition();
enemy1.updatePosition();
system("cls");
fsDraw(playerAircraft);
fsDraw(mybullet);
fsDraw(enemy1);
fsDraw(enemybullet);
fsDrawScoreBoard(scoreBoard);
if (fsOverlap(mybullet, enemy1)) {
scoreBoard.addScore();
mybullet.setVisible(0);
if (fsOverlap(enemybullet, playerAircraft)) {
cout && "you are hit!";
enemybullet.setVisible(0);
Sleep(500);
Sleep(FS_DEFAULT_FRAME_INTERVAL);
  这次大作业大概花了三四天时间,这个过程中,我在可视化界面的选择上比较纠结,本想用QT,但感觉学QT也要花不少时间,而且画面也不是这门课程的重点,所以最后选择了控制台来做可视化界面,虽然效果不太理想,但至少可以运行。
  在大作业的过程中,我不仅运用了课上学习的关于模板和类的知识,还自学了有关控制台绘制的控制等技能,让我初步体验了完成一个项目是什么样的经历,很有意义。在写说明文档的过程中,我发现Word在处理代码的时候非常麻烦,又学习了Markdown的使用,也算是另外一个小小的收获吧。
  这次的作业虽然已经可以运行,但离真正的飞行射击游戏还有很大的差距,比如,画面不够精美,没有升级制度,没有设置多个敌机,没有Boss,不支持PVP(玩家VS玩家)等,以后我还会再抽时间不断完善。值得一提的是,本来想要使用双缓冲技术避免屏幕闪烁,但这样就难以显示出完整的画面,所以最后我搁置了这个方案,这点可能是我首先需要完善的地方。
最新代码获取
请访问我的github:
这里包含了VS2015的解决方案,可以直接编译运行。
C++游戏系列:目录
VS下用C++实现的简单3D射击游戏(附游戏文件与源工程代码)
C++ 飞行射击游戏设计全程实录
用C++语言写游戏——打飞机
会转弯的子弹------跟踪弹的实现
没有更多推荐了,收藏的论坛
这样“打飞机”才过瘾!细数PC上的飞行射击游戏
1  之前微信更新版本的时候,大家还记得登陆的时候那个&打飞机&的吗?当时网上都在说,把分数超过自己的好友删掉,这样自己就是第一名了。像这种打飞机的小游戏还是非常容易上手的,不过要是换成大型的打飞机游戏,那就完全是另一种感觉了,不仅场景火爆,而且漫天的子弹,还十分刺激,上手度就难多了。下面小编就为大家推荐几款这类模拟飞行的游戏,毕竟这和我们常见的游戏类型还是不太一样,肯定会有种别样的乐趣。  《汤姆克兰西之》  英文名称:H.A.W.X 2  游戏类型:飞行射击  制作发行:UbiSoft  登陆平台:PC  游戏语言:中文  游戏介绍:Ubisoft系列新作《汤姆克兰西之2》里玩家将扮演隶属于机密部队H.A.W.X.2的杰出飞行员,游戏中莫斯科、波斯湾、北极圈、印度洋等场景都是以商业卫星提供的高清晰卫星照片为基础制作出来的。在游戏中,可玩部分包括飞机的起飞和降落、空中侦察、空中加油、近距离空战、长途奔袭轰炸等丰富多彩的设定。
2  《:突击地平线》  英文名称:Ace Combat Assault Horizon  游戏类型:飞行射击  制作发行:Namco Bandai  登陆平台:PC  游戏语言:中文  游戏介绍:《皇牌空战7:突击地平线》具有和系列过去的系统完全不同的爽快感和速度感。被击坠的敌机碎片飞舞,就像身处空战前线在体验真实的战争。该作的亮点是动作性与射击性同时重视的&交叉范围攻击&系统。操作方法直观,重视魄力表现的视角,像电影版充满紧张感的战斗乐趣。画面中央的圆环会在敌机进入后进行自动瞄准和自动机枪攻击。此外还可以操纵导弹和火箭炮等多彩的武器。3  《阿帕奇:空中突击》  英文名称:Apache Air Assault  游戏类型:飞行射击  制作发行:Gaijin Entertainment  登陆平台:PC  游戏语言:中文  游戏介绍:由Gaijin Entertainment开发,动视发行的直升机模拟游戏《阿帕奇:空中突击》,其中长弓AH-64D型阿帕奇攻击直升机是坦克的克星、步兵的灾星。游戏将支持空对空、空对地等作战模式,地域将包括美洲中的丛林和亚洲中的山区。你可以进行单人游戏、与朋友合作(另一个位将是副驾驶)可执行16项任务。  《》  英文名称:Thunder Wolves  游戏类型:飞行射击  制作发行:UbiSoft  登陆平台:PC  游戏语言:中文  游戏介绍:碧由&最高通缉&制作商打造的直升机动作游戏《雷狼》。玩家将跟随&雷狼&团队打击全球的恐怖活动,例如护送任务,战场援助等。该作包含30种的直升机和武器,支持升级系统。本作拥有战役模式,合作模式,可谓是非常惊现火爆。4  《超音速4》  英文名称:HyperSonic 4  游戏类型:飞行射击  制作发行:GameCrafterTeam  登陆平台:PC  游戏语言:英文  游戏介绍:《超音速4》是由GameCrafterTeam制作发行的一款飞行射击类游戏,这部作品在画面和飞行模拟方面都不是很完美,但是对于不过分追求完美的玩家来说已经足够了,追踪目标,导弹射击,一个也不少。有许多游戏模式都与其他空战游戏有着相同之处,追踪敌人时你经常需要不停的旋转来寻找目标,你的屏幕将会再次出现天旋地转的画面,容易头晕的玩家千万要悠着点。  《攻击战斗机2》  英文名称:Strike Fighters 2  游戏类型:飞行射击  制作发行:Thirdwire  登陆平台:PC  游戏语言:英文  游戏介绍:《攻击战斗机2》是款耐玩性超高的空战类射击游戏,游戏以逼真的3D渲染方式进行,玩家可感受到这款飞行游戏的独特魅力。在游戏中除了普通的飞机品种外,玩家还可驾驶传说中的苏联造的米格喷射机来射击敌人,玩家必须在规定的时间内完成任务,任务类型有拦截、巡逻、护航、压制、侦察、反舰等十多种丰富的任务,按时完成任务才能迎接下关的挑战,能否完成任务这需要你各种毅力了。  《欧洲空战英雄》  英文名称:Heroes Over Europe  游戏类型:飞行射击  制作发行:Atari  登陆平台:PC  游戏语言:英文  游戏介绍:游戏由墨尔本的Transmission Games工作室开发,《欧洲空战英雄》将提供一种叫做ace kill的近距离战斗动作,这将大大提高空战游戏的可玩性。游戏有四种在线模式,最多可以支持16名玩家同时游戏,而且你还能随心所欲来打扮你的战斗机。游戏中的高分辨率地图还将伦敦,柏林以及法国阿尔卑斯山脉等主要战区场景进行了相似度极高的还原。
5  《微软模拟飞行10》  英文名称:Microsoft Flight Simulator X  游戏类型:模拟飞行  制作发行:Microsoft Games  登陆平台:PC  游戏语言:中文  游戏介绍:《微软模拟飞行10》由微软游戏工作室发行,ACES studio开发,这款作品是《模拟飞行》系列的第十代,这个系列是模拟飞行游戏的王者,也是PC游戏史上最为著名的系列游戏之一。《微软模拟飞行10》确立了为模拟飞行游戏确立了技术标准,在画面和拟真程度上达到了顶峰。《微软模拟飞行10》中,玩家可以驾乘更多的各式飞机在极为壮观而真实的天空中飞行,可以与游戏中的飞行员一起欣赏美丽的空中景色。在《微软模拟飞行10》中,玩家的梦想都可以成真,你可以驾驶de Havilland Beaver和Grumman Goose等等水上飞机体验一下水上飞机的驾乘感觉。  《破坏连队:太平洋中队W》  英文名称:Dae Inc. Pacific Squadron WWII  游戏类型:飞行射击  制作发行:Mad Catz  登陆平台:PC  游戏语言:中文  游戏介绍:《破坏连队:太平洋中队WWII》这款以二战为题材的飞行模拟游戏确实是属于不多见的种类,对于喜欢二战题材游戏同时又对飞行模拟感兴趣的玩家来说,这款游戏绝对是最佳的选择。游戏中玩家不仅有机会驾驶多种飞行,同时还将在游戏参与一些战役,游戏中的战争场面设计的很逼真也很宏大,相信能带给玩家非常棒的二战体验。  《壮志凌云:硬锁》  英文名称:Top Gun Hard Lock  游戏类型:飞行射击  制作发行:505 Games  登陆平台:PC  游戏语言:中文  游戏介绍:《壮志凌云:硬锁》是Headstrong Games制作并由代理商505 Games发行的一款动作射击类!本作将包含F-14喷气战斗机和其他高速许可机型。另外游戏还将引入硬锁&HardLock&模式,玩家需要进行策略战,不同策略所产生的效果将令玩家沉醉。
近期游戏热闻求一款飞行射击街机游戏,飞机尾部可以吃到很多圆球,每一个圆球就是一种特殊子弹,比如糖葫芦一样来回扫_百度知道
求一款飞行射击街机游戏,飞机尾部可以吃到很多圆球,每一个圆球就是一种特殊子弹,比如糖葫芦一样来回扫
子弹,从下往上清屏的激光等等...
子弹,从下往上清屏的激光等等
答题抽奖
首次认真答题后
即可获得3次抽奖机会,100%中奖。
采纳数:10448
获赞数:37619
飞机尾部挂东西的记得有两个游戏捉虫特工队
魔法大作战
采纳数:2726
获赞数:14486
你要找的游戏是:捉虫敢死队
擅长:暂未定制
那个游戏叫 三个世界 我记得没错的话 里面有3个游戏 第一个是2个小人打枪的 第2个是你说的这个游戏 第3个事 小兔子 推箱子的
采纳数:78
获赞数:195
不知道你说的是什么 也许是四国战机 也许是太空争霸
其他1条回答
为你推荐:
其他类似问题
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。使用Cocos2dx-JS开发一个飞行射击游戏 - 简书
使用Cocos2dx-JS开发一个飞行射击游戏
笔者闲来无事,某天github闲逛,看到了游戏引擎的专题,引起了自己的兴趣,于是就自己捣腾了一下Cocos2dx-JS。
由于是学习,所谓纸上得来终觉浅,只是看文档看sample看demo,并不会让自己有多大的提升,于是一开始就计划写一个小游戏,以作为自己完成这个阶段学习的一个标志,也算是目标导向吧。
完整源码移步Github:
Online Demo:
(由于需要WebGL的支持,请使用较新版本的Chrome或者Firefox打开)
本游戏的图片素材以及特效素材来自腾讯的《全民飞机大战》,仅供学习参考,请勿用于其他用途。
笔者水平有限,本身也不是专业游戏开发者,如有错漏,还请多多指教。
二、Cocos2dx-JS
Cocos2dx的介绍请移步官网:
目前Cocos2dx支持3种开发语言:C++、Lua、 JavaScript,其中C++和Lua是使用较为广泛开发语言,而JS的支持最早是作为独立的项目进行开发的,在3.7之后并入了Cocos2d-x的主repository,成为了官方支持的正式开发语言。
使用JS开发Cocos2dx游戏的优点:
跨平台,是唯一可以到达web端的分支,同时也支持编译成iOS和Android原生APP
开发效率高,开发难度相对较低
运行效率相对较低
JS-binding在功能和文档以及开发者的使用经验上仍然落后于C++和Lua
阅读本文之前,你最好具备以下知识基础:
基本的JavaScript技能
Cocos2dx-JS的基本知识
三、游戏简介
这个游戏是一个简单的飞行射击游戏,主要使用鼠标点击页面(移动设备就是手指点击屏幕)控制飞机的飞行,从而实现攻击敌人,回避攻击,获取增强效果物品等常见的行为。
具体细节的可以到Online Demo去看一看。
四、整体设计
1. Game Scene
游戏总共有3个scene;Menu、About、Game,可以理解为不同的功能放在不同的场合之中,其中最重要的是菜单(Menu)场景和游戏(Game)场景,又以游戏场景最为核心,当我们控制飞机进行游戏的时候,实际上就是在于游戏场景在进行交互,此时我们看到的飞机,敌人,子弹,特效,背景等等一切,都是游戏场景在承载。
2. Game Layer
Layer的层次比Scene低一级,通常是一个scene会包含一个或者多个layer(当然也可以一个都没有)。
通常不同的内容可以放在不同的layer中,比如游戏场景里面,就使用了一个独立的layer来显示飞机的血量和得分。
本游戏只有3个layer,GameLayer,GameInfo 和 GameOverLayer,顾名思义,一个作为游戏场景的承载,一个显示飞机的血量和得分,一个在飞机死亡的时候显示相关的信息。
3. Game Object
使用了面向对象的设计来组织游戏的各个对象:
Plane:玩家控制的飞机
Enemy:敌人的基类,提供了公共行为接口,包括回收与重用的接口
Bullet:子弹的基类,提供了公共行为接口,包括回收与重用的接口
./Enemies:扩展Enemy基类,不同的敌人有自己的行为
./Bullets:扩展Bullet基类,不同的子弹的效果自己定义
./Boss:扩展Enemy基类,实际上就是特殊的敌人作为Boss登场
4. Manager
设计了若干个manager来组织游戏的各个方面:
BulletManager:每帧更新子弹的位置,检测碰撞和伤害,回收超出屏幕的子弹
EnemyManager:控制敌人和Boss的产生,检测敌人与飞机的碰撞,控制敌人的攻击,回收敌人
ItemManager:控制游戏中随机增强item的产生,并应用其效果
MusicManager:音效的控制
ParticleManager:特效的控制,包括被攻击的特效,item的特效,Boss特殊攻击的特效
StageManger:控制游戏阶段,比如常规阶段,boss阶段等等
五、启动游戏
启动游戏,我们需要设置game stage的配置以及初始化各个manager,并且以Menu Scene作为第一个scene:
var defaultStage = [{name:"normal", duration: 20}, {name:"mega", duration: 20}, {name:"boss", duration:100000}]; //game stage配置
cc.game.onStart = function(){
cc.view.adjustViewPort(false);
cc.view.setDesignResolutionSize(512, 768, cc.ResolutionPolicy.SHOW_ALL); // 设置分辨率
cc.view.resizeWithBrowserSize(true);
//load resources
cc.LoaderScene.preload(g_resources, function () {
cc.game.musicManager = new MusicManager(); //初始化各个manager
cc.game.bulletManager = new BulletManager();
cc.game.enemyManager = new EnemyManager();
cc.game.itemManager = new ItemManager();
cc.game.particleManager = new ParticleManager();
cc.game.stageManager = new StageManager(defaultStage);
cc.director.runScene(new MenuScene()); //首先启动Menu Scene
cc.director.setDisplayStats(false);
cc.game.run(); //运行游戏
var trace = function() {
//cc.log(Array.prototype.join.call(arguments, ", "));
六、Menu Scene
Menu场景只需要一个背景图片,一段BGM,以及一个按钮开始游戏即可
var MenuScene = cc.Scene.extend({
ctor:function(){
this._super();
var layer = new cc.Layer();
this.addChild(layer);
var winSize = cc.director.getWinSize();
cc.game.winSize = winS
var bgImage = new cc.Sprite("res/bg.png"); //背景图片
bgImage.x=winSize.width/2;
bgImage.y = winSize.height/2;
layer.addChild(bgImage);
var gameName = new cc.LabelTTF("Jason's Ambition", "Arial",58, cc.Size(500, 200), cc.TEXT_ALIGNMENT_CENTER, cc.VERTICAL_TEXT_ALIGNMENT_TOP); //标题文本
gameName.x = winSize.width/2;
gameName.y = winSize.height+100;
var gameNameAction = cc.moveTo(3, cc.p(winSize.width/2, winSize.height-200)); //让标题动起来
gameName.runAction(gameNameAction);
layer.addChild(gameName);
this.scheduleOnce(function(){
this.showMenu();
}.bind(this),3); //3秒之后显示按钮
cc.game.musicManager.playStarWar();
showMenu:function(){
var startGameButton = new cc.MenuItemImage("res/startgame2.png", "res/startgame2.png", this.startGame, this); //创建按钮并绑定回调事件
var menu = new cc.Menu(startGameButton);
this.addChild(menu);
startGame: function(){
trace('Game start..');
cc.director.runScene(new AboutScene()); //点击按钮后跳到About Scene
在这个场景里面,我们加载了背景图片,播放了BGM,设置了会移动的标题,并在标题停止移动后显示开始按钮,在按钮上绑定回调事件,点击后跳转到About Scene。
七、About Scene
About Scene只是一段文本,3秒后跳转到游戏场景。
var AboutScene = cc.Scene.extend({
ctor:function(){
this._super();
var winSize = cc.director.getWinSize();
var layer = new cc.Layer();
this.addChild(layer);
var aboutText = "Jason, a shameless man.\nHe want to kidnap all girls around the earth.\n";
aboutText += "Hero, you have to stop him!";
var aboutLabel = new cc.LabelTTF(aboutText, "Arial",24, cc.Size(500, 200), cc.TEXT_ALIGNMENT_CENTER, cc.VERTICAL_TEXT_ALIGNMENT_TOP);
aboutLabel.x = winSize.width/2;
aboutLabel.y = winSize.height-300;
layer.addChild(aboutLabel);
this.scheduleOnce(function(){
var gameScene = new GameScene();
cc.director.runScene(gameScene); //启动游戏场景,游戏开始
cc.game.gameScene = gameS
八、Game Scene
Game Scene的设计是最为复杂的,因为是游戏交互的场所,所以其之上需要处理的事情也最多。
我们需要一点一点说。
GameScene的执行流程:(简书不支持流程图,有兴趣的同学可以把下面的代码复制到支持flowchart流程图的Markdown编辑器里面查看,注意使用flow代码段包裹,例如)
st=&start: Start Game Scene
end=&end: End Game
addBackGround=&operation: Add scrolling background
addPlane=&operation: Add plane
listenClick=&operation: Listen to click operation
continueGame=&condition: Plane dead or Boss dead?
addEnemy=&operation: Add new enemies
enemyAttack=&operation: Enemy attack
bulletReuse=&operation: Reuse out bullets
bulletHit=&operation: Check collision between planes and bullets
bulletUpdate=&operation: Update bullets' action
enenmyCollide=&operation: Check the collision between enemies and plane
enemyOut=&operation: Reuse outing enemies
updateInfo=&operation: Update game information
genenrateItem=&operation: Generate items
itemCollide=&operation: Check the collision between items and plane
allyEffect=&operation: Apply items' effect to plane
planeBlood=&condition: Plane's dead?
gameOver=&operation: Game Over
st-&addBackGround-&addPlane-&listenClick-&continueGame
continueGame(yes)-&addEnemy-&enemyAttack-&bulletReuse-&bulletHit-&bulletUpdate-&enenmyCollide-&enemyOut
enemyOut-&updateInfo-&genenrateItem-&itemCollide-&allyEffect-&planeBlood
planeBlood(yes)-&gameOver
planeBlood(no)-&continueGame
continueGame(no)-&gameOver
gameOver-&end
首先解释帧的概念。
对于一个游戏而言,连贯的画面其实是有快速切换的帧造成的,而每一帧之间会有细小的变化,连续播放帧就会形成动作。而帧之间的变化,就是我们的代码需要计算和处理的。
简而言之,如果你需要一个物体移动到某个地方,那你的代码就应该在每一帧里面对他的坐标进行一些改变,从而实现移动这个效果(当然,任何一个优秀的游戏引擎,这种基本的动画是不需要我们自己去做帧间的处理的,我们只需要调用一次,这个效果就会在它所进行的时间内在每帧进行更新)。
在Scene的ctor函数里面调用this.scheduleUpdate 方法,就会使用默认的update方法去进行帧间计算,我们在update方法里面需要做的事情:
更新飞机的位置
更新敌人的位置
更新子弹的位置
更新item的位置
判断飞机与敌人、子弹、item的碰撞,并应用效果
其中,实际上我们只需要真的去在每一帧都处理的只有“更新子弹的位置” 以及 “判断飞机与敌人、子弹、item的碰撞,并应用效果”其余的都只需要在他们真的需要更新的时候声明一次action,之后的每帧,cocos都会帮我们去移动,而不需要我们再去计算改怎么移动。
如果说“判断飞机与敌人、子弹、item的碰撞,并应用效果”的处理是理所当然的,那么为什么“更新子弹的位置”不能交给cocos去处理?事实上大多数的子弹的位置也都是有cocos去应用action,我们只需要在子弹发生的时候告诉cocos接下来怎么去移动子弹就可以了,但是有例外的情况:追踪弹。它的移动和敌人相关,因此在敌人的位置、存活发生变化的时候可能需要改变它的移动,这个时候就需要我们在帧间进行处理:计算出新的移动方向,告诉cocos取消之前的action,应用新的action。
update函数的执行时间与游戏的帧率是息息相关的,因为游戏的每一帧都是根据这个函数的计算结果来产生的,如果它的执行时间太长,就会导致帧率下降。
举个例子,如果update的执行时间为0.1秒,那么这个游戏最高也只可能是10fps,因为在1秒内它最多执行10次,给出10次的结果,从而cocos也只能由此渲染10帧的画面。
总而言之,如果你想实现什么效果,那就在update function里面计算这个效果在每一帧里面的作用,然后让cocos去把帧渲染出来,联合起来就可以实现你要的效果。
2. 游戏背景
首先,作为一个飞行射击游戏,我们需要一个可以滚动的背景。自然,准备一张高度足够的图片,让它从上往下匀速下移是可以实现这个效果的,但是实际上应该不会有人会这样做。
在这里,实际上我们只需要一张图片,这张图片刚好可以覆盖整个游戏的可是窗口就可以了:
游戏背景图
如果你仔细观察,你会发现,这张图片的上面和下面是可以接合的,也就是说,如果你在这张图片的正上方再放一张同样的图片,你会发现这两张图片的景色、河流都是刚好接合起来,看不出是两种图片,而像是一张图片。
事实上,我们正式利用这样的图片来实现背景的无限滚动。
我们只需要在一开始就放一张图片铺满显示窗口,然后在窗口的正上方再放一张一样的图片,让它们同时匀速往下移动,等到第一张图片完全离开窗口,而第二种刚好铺满窗口的时候,在下一帧恢复它们的初始位置,不断重复这个过程,那么看起来背景就是在不断的移动。
_scrollBackground:function(){
this.bgImage1.y -= Math.ceil(this.winSize.height*0.001);
//下移两张图片的位置
this.bgImage2.y -= Math.ceil(this.winSize.height*0.001);
if(this.bgImage1.y&-(this.winSize.height/2)){
//当第一张图片的y小于负的二分之一屏幕高度,就是它刚好离开视窗的时候,此时恢复两张图片的初始位置
this.bgImage1.y = this.winSize.height/2;
//第一张置于中间
this.bgImage2.y = this.winSize.height/2+this.winSize.
//第二张放着第一张的上面
3. 添加/移动 飞机
飞机本身只是一个Sprite:
var Plane = cc.Sprite.extend({
image:null,
bullet:[],
status:null,
blood:5000,
layer:null,
gameScene:null,
shotInterval:200,
allowShot:true,
allowMissile:true,
allowUltra:true,
hitEffect:null,
addedEffects:[],
laserHit:true,
ctor:function(gameScene, layer){
this._super("res/main_plane.png"); //初始化飞机的图片
this.gameScene = gameS
this.layer =
this.addedEffects = [];
this.allowShot =
this.allowMissile =
this.allowUltra =
this.laserHit =
在Game Scene中添加飞机:
var plane = new Plane(this, layer);
plane.scale = 0.5;
this.targetX = plane.x = winSize.width/2;
this.targetY = plane.y = winSize.height/2;
layer.addChild(plane,7);
//给game scene的layer添加飞机
this.plane =
这样在game scene上就会出现飞机的图片,但是仅仅只是这样是不够的,我们需要这个飞机可以根据我们的点击进行移动,原理也很简单,就是获取点击的坐标,然后让飞机移动过去,这里我们需要监听两个事件:
在game scene里监听事件:
if("touches" in cc.sys.capabilities){
cc.eventManager.addListener({
event: cc.EventListener.TOUCH_ONE_BY_ONE,
onTouchBegan: this._onTouchBegan.bind(this)
//处理触摸的回调
cc.eventManager.addListener({
event: cc.EventListener.MOUSE,
onMouseDown: this._onMouseDown.bind(this)
//处理鼠标点击的回调
在回调里使用点击的坐标让飞机移动:
_onTouchBegan:function(touch, event){
var clickX = touch.getLocation().x;
var clickY = touch.getLocation().y;
this.targetX = clickX;
this.targetY = clickY;
var speedo = Math.sqrt(Math.pow(clickX-this.plane.x,2)+Math.pow(clickY-this.plane.y,2))/300;
var moveAction = cc.moveTo(speedo, cc.p(clickX, clickY));
if(this.action!=null)
this.plane.stopAllActions();
this.plane.runAction(moveAction);
this.action = moveA
_onMouseDown: function(event){
var clickX = event.getLocationX();
var clickY = event.getLocationY();
this.targetX = clickX;
this.targetY = clickY;
var speedo = Math.sqrt(Math.pow(clickX-this.plane.x,2)+Math.pow(clickY-this.plane.y,2))/300;
var moveAction = cc.moveTo(speedo, cc.p(clickX, clickY));
if(this.action!=null)
this.plane.stopAllActions();
this.plane.runAction(moveAction);
this.action = moveA
这样,我们点哪里,飞机就会移动到哪里。
但是此时的飞机只能够移动,并不能攻击。
4. 添加/管理敌人
考虑到敌人可能有多种多样,每种敌人会有自己的子弹、运动轨迹,所以我们先定义一个基类,里面定义了敌人的通用接口,具体的实现在每个敌人的之类里面完成。
var Enemy = cc.Sprite.extend({
bullets:null,
scene:null,
layer:null,
score:null,
blood:null,
explosionHarm:null,
//ctor:function(image){
// this._super(image);
appear:function(){},
//敌人出场
attack:function(){},
explode:function(){},
hurt:function(){},
//被攻击受到伤害
move:function(){},
//敌人移动
reuse:function(scene, layer){
//reuse和unuse方法用于敌人的缓存回收,这部分将独立说明
trace("reuse enemy:");
this.ctor(scene, layer);
unuse:function(){
this.layer.removeChild(this);
Alpha(一个Enemy的子类,是一个具体的敌人)的部分实现:
var Alpha = Enemy.extend({
image:"res/enemy/alpha.png",
allowShot:true,
blood:null,
score:100,
explosionHarm:100,
ctor:function(scene, layer){
//初始化Alpha,并调用appear方法,使得敌人在游戏场景中登场
this.allowShot =
this.scene =
this.gameScene = this.
this.layer =
this._super(this.image);
this.blood = 50;
this.threshold = 50;
this.appear();
layer.removeChild(this);
if(scene.enemies.indexOf(this)!=-1)
scene.enemies.splice(scene.enemies.indexOf(this), 1);
layer.addChild(this, 2);
scene.enemies.push(this);
//用一个数组存储所有的敌人,在之后的攻击判定、清理回收等会用到
appear:function(){
//登场,在可是窗口的上方的一个随机位置里面出现
this.scale = 0.5;
this.x = Math.round(Math.random()*cc.game.winSize.width);
this.y = cc.game.winSize.height+100;
this.move();
//登场后移动,移动到可视窗口下方的某个随机位置
move:function(){
var moveAction = cc.moveTo(12, cc.p(Math.random()*cc.game.winSize.width,-400));
this.runAction(moveAction);
//移动敌人
这样,如果我们创建一个Alpha的实例,那么它就会出现在游戏场景,并且从游戏窗口的上方的某个位置匀速地向下移动。
EnemyManager
为了更好地管理敌人的生成和回收,我们创建了一个enemyManager来管理敌人的添加、移除回收、碰撞检测:
var EnemyManager = cc.Class.extend({
allowAddNewEnemy: true,
privateSprite:null,
enemyList:[
enemyFrequency:1,
ctor: function () {
this.privateSprite = new cc.Sprite();
this.allowAddNewEnemy =
addEnemy: function (scene, layer, frequency) {
//负责添加新敌人
if (this.allowAddNewEnemy == false)
//new Alpha(scene, layer);
//trace("New enemy");
this.allowAddNewEnemy =
var enemyType = this.enemyList[new Date().getTime() % this.enemyList.length];
if(cc.pool.hasObject(enemyType)){
cc.pool.getFromPool(enemyType, scene, layer);
trace("reuse enemy");
new enemyType(scene, layer);
scene.scheduleOnce(function () {
trace("allow new enemy");
this.allowAddNewEnemy =
.bind(this),this.enemyFrequency);
addBoss : function(scene, layer){
//添加BOSS
new Jason(scene, layer);
removeEnemy:function(scene, layer, enemy, index){
//从场景里面删除一个敌人
scene.score+=enemy.
layer.removeChild(enemy);
scene.enemies.splice(index, 1);
cc.pool.putInPool(enemy);
enemyAttack:function(scene){
//命令所有敌人进行一次攻击
for(var i =0;i&scene.enemies.i++){
scene.enemies[i].attack();
_collide:function(x1, y1, x2, y2, threshold){
//碰撞检测
if((Math.pow((x1-x2),2)+Math.pow((y1-y2),2))&Math.pow(threshold, 2))
collisionCheck:function(scene, layer, plane){
//如果敌人受到攻击,且血量低于0,让敌人爆炸,同时回收敌人
var enemies = scene.
for(var i = 0;i&enemies.i++){
var checkEnemy = enemies[i];
if(this._collide(checkEnemy.x, checkEnemy.y, plane.x, plane.y, checkEnemy.threshold) == true){
plane.blood -= checkEnemy.explosionH
scene.score += checkEnemy.
checkEnemy.explode();
this.removeEnemy(scene, layer, checkEnemy, i);
outEnemyCheck : function(scene, layer){
//如果敌人飞出了游戏场景,也回收敌人
var enemies = scene.
for(var i = 0;i&enemies.i++){
var checkEnemy = enemies[i];
if(checkEnemy.x&-200 && checkEnemy.x&cc.game.winSize.width+200 && checkEnemy.y&-200 && cc.game.winSize.height+200)
trace("unuse out ennemy");
this.removeEnemy(scene, layer, checkEnemy, i);
5. 子弹系统
飞机与敌人都可以发射子弹,我们需要解决的问题是:
子弹的生成、发射(位置,角度)、速度、伤害,动画
子弹与飞机/敌人的碰撞检测
子弹的回收
Bullet基类
与Enemy类似地,基类只定义了接口以及共同的方法:
var Bullet = cc.Class.extend({
BulletOwner:null,
type:null,
plane:null,
layer:null,
action:null,
bullets:null,
scene:null,
ctor:function(type, plane, layer, scene){
//根据给出的type初始化bullet
this.type =
this.plane =
this.layer =
this.scene =
this.bullets = [];
this._shot();
//初始化完毕后马上发射子弹,_shot方法应该在子弹之类里面具体实现
_validate:function(){
//检测子弹是否离开了可视窗口
var valid =
for(var i =0;i&this.bullets.i++){
var childBullet = this.bullets[i];
if(childBullet.x&=-20 && childBullet.x&=cc.game.winSize.width+20 && childBullet.y&=0 && childBullet.y&=cc.game.winSize.height+20){
reuse:function(type, plane, layer, scene){
//reuse和unuse用于回收、重新利用子弹
this.ctor(type, plane, layer, scene);
unuse:function(){
for(var i =0;i&this.bullets.i++){
this.bullets[i].stopAllActions();
this.layer.removeChild(this.bullets[i]);
_shot:function(){}
BasicBullet
BasicBullet是一个具体实现的子弹之类,设置了子弹的图片、数量、发射位置,实现了_shot方法:
var BasicBullet = Bullet.extend({
BulletType:{
"basic" : "res/bullet/bullet_basic.png"
//子弹的图片
BulletLocation : {
"basic" : [{x:0, y:0}]
//数组的元素数量代表子弹的数量,x和y坐标代表某个子弹的发射位置
BulletOwner:"enemy",
//子弹的伤害
_shot:function(){
//_shot方法的作用:创建子弹Sprite,添加到scene中,调用runBulletCustomAction使得子弹开始运动
for(var i =0;i&this.BulletLocation[this.type].i++){
var bulletLocation = this.BulletLocation[this.type][i];
var newBullet = new cc.Sprite(this.BulletType[this.type]);
newBullet.scale = 0.15;
newBullet.x = this.plane.x+bulletLocation.x;
newBullet.y = this.plane.y+bulletLocation.y;
newBullet.harm = this.
this.runBulletCustomAction(newBullet);
this.layer.addChild(newBullet, 1);
this.bullets.push(newBullet);
this.layer.bullets.push(this);
runBulletCustomAction:function(newBullet){
//实现了每种子弹自己的运动方式,不同的子弹的不同行为在这里定义
var action = cc.moveTo(4, cc.p(newBullet.x, -(cc.director.getWinSize().height+300)));
var rotation = cc.rotateBy(10,);
newBullet.runAction(cc.spawn(action,rotation));
实际的子弹种类会有多种,每个子弹的飞行轨迹也都各有不同。BasicBullet的飞行轨迹从初始化就已经决定了,以后不再改变,直到发生碰撞或者离开窗口被回收。
但是也可以实现更复杂的飞行轨迹,比如跟踪弹(MissileBullet)就会跟踪离它最近的敌人的位置,这个子弹的运动就需要每帧更新):
MissileBullet
var MissileBullet = Bullet.extend({
updatePerFrames:true,
//是否需要每帧更新,对于MissileBullet来说是true
allowSwitchEnemy:false,
//是否允许在原追踪敌人毁灭后更换瞄准目标
aimedEnemy:function(bulletX, bulletY, oldX, oldY, bullet){
//瞄准敌人,每帧都会计算当前离追踪弹最接近的敌人是哪一个,在allowSwitchEnemy为true时会在敌人摧毁后重新计算,否则将会沿着之前的轨迹方向飞行
var enemyX = 0;
var enemyY = 0;
var foundEnemy =
if(this.allowSwitchEnemy == true || bullet.lockEnemy == false ) {
if (this.scene.enemies != null && this.scene.enemies.length & 0) {
var enemyList = this.scene.
var leastDistance = ;
var leastX = bulletX;
var leastY = cc.game.winSize.height + 200;
for (var i = 0; i & enemyList. i++) {
var checkEnemy = enemyList[i];
if (checkEnemy.x & 0 && checkEnemy.x & cc.game.winSize.width && checkEnemy.y & 0 && checkEnemy.y & cc.game.winSize.height)
var distance = Math.pow((bulletX - checkEnemy.x), 2) + Math.pow((bulletY - checkEnemy.y), 2);
if (distance & leastDistance) {
foundEnemy =
bullet.lockEnemy =
bullet.enemy = checkE
leastDistance =
leastX = checkEnemy.x;
leastY = checkEnemy.y;
enemyX = leastX;
enemyY = leastY;
if(bullet.enemy != null && this.scene.enemies.indexOf(bullet.enemy) & -1 ){
foundEnemy =
enemyX = bullet.enemy.x;
enemyY = bullet.enemy.y;
if(foundEnemy != true){
var deltaX = bulletX - oldX;
var deltaY = bulletY - oldY;
var deltaD = Math.sqrt(Math.pow(deltaX,2)+Math.pow(deltaY,2));
enemyX = bulletX + this.speed*deltaX/deltaD;
enemyY = bulletY + this.speed*deltaY/deltaD;
var rotationAngle = Math.atan((enemyY - bulletY)/(enemyX - bulletX))*360/(2*Math.PI);
if((enemyX - bulletX)&=0)
rotationAngle += 180;
if(rotationAngle&0)
rotationAngle +=360;
rotationAngle = 90 - rotationA
//trace((enemyY - bulletY)+","+(enemyX - bulletX)+","+rotationAngle);
//trace(enemyY+","+(bulletY)+","+(oldY));
var duration = Math.sqrt(Math.pow(enemyX - bulletX, 2) + Math.pow(enemyY - bulletY, 2))/this.
//trace("bullet speed:"+duration);
return {x:enemyX, y:enemyY, duration:duration, rotationAngle:rotationAngle};
perFramesUpdate:function(scene, layer, plane, bullet){
//每帧更新,每一帧都根据瞄准的敌人的位置调整子弹的运动轨迹
if(bullet.initial == true) {
bullet.scheduleOnce(function(){
this.initial =
}.bind(bullet), 0.2);
//if(Math.abs(bullet.x - bullet.oldX)&10 || Math.abs(bullet.y - bullet.oldY)&10)
//if(bullet.moveAction!=null)
// bullet.moveAction.speed(10000);
if(bullet.recentStop == null || bullet.recentStop == false) {
bullet.stopAllActions();
bullet.recentStop =
bullet.scheduleOnce(function(){
this.recentStop =
}.bind(bullet), 0.02);
//if(bullet.moveAction!=null)
// trace("speed:"+bullet.moveAction.getSpeed());
//bullet.stopAllActions();
var enemyLocation = this.aimedEnemy(bullet.x, bullet.y, bullet.oldX, bullet.oldY, bullet);
bullet.oldX = bullet.x;
bullet.oldY = bullet.y;
//trace("now:"+bullet.x+","+bullet.y);
//trace(enemyLocation.x+","+enemyLocation.y);
var action = cc.moveTo(enemyLocation.duration, cc.p(enemyLocation.x, enemyLocation.y));
//var action = cc.moveTo(1, cc.p(bullet.x+1000, bullet.y+1000));
//action.easing(cc.easeIn(20));
//bullet.setRotationSkewX(enemyLocation.rotationAngle);
var rotationAction = cc.rotateTo(0.01,enemyLocation.rotationAngle);
//trace("angel:"+enemyLocation.rotationAngle);
bullet.runAction(cc.spawn(action, rotationAction));
bullet.moveAction =
在飞机和敌人上发射子弹,是通过调用plane._shot()和enemy.attack()来进行的,他们的实现其实是类似的:
飞机的_shot方法里有:
if (this.allowShot) {
//cc.game.musicManager.playEffect("bullet");
if (cc.pool.hasObject(DefaultBullet)) {
//从回收池里取出DefaultBullet实例
cc.pool.getFromPool(DefaultBullet, "normal", this, this.layer, this.gameScene);
//trace("reuse bullets");
//没有就new一个DefaultBullet的实例
new DefaultBullet("normal", this, this.layer, this.gameScene);
this.allowShot =
this.scheduleOnce(function () {
//每0.2秒设计一次
this.allowShot =
.bind(this), 0.2);
飞机发射的子弹和敌人之间,以及敌人的子弹与飞机之间,需要进行碰撞检测,如果发射了碰撞,就需要回收子弹,计算伤害(并且根据血量摧毁敌人/飞机,同时显示特效等)。
碰撞检测的方法有很多,这里我们只使用了最简单的方法:计算子弹和机体的距离,就是根据两个目标的x轴和y轴运用勾股定理计算距离,当距离小于一定的阈值(threshold)时判定为发生碰撞。
碰撞一共有3种:
子弹与机体
敌人与飞机
物品与飞机
这3种碰撞分别由3个manager来执行检查:BulletManager,EnemManager,ItemManager。
以BulletManager的子弹与机体的碰撞检测为例:
collisionCheck:function(scene, layer){
//分别进行飞机与敌人的子弹,飞机的子弹与敌人的碰撞检测,计算伤害,在血量为0时调用机体的explode方法,回收敌人,同时进行子弹回收
var playerX= scene.plane.x;
var playerY= scene.plane.y;
var bullets = layer.
for(var i =0;i&bullets.i++) {
var checkBullet = bullets[i];
if (checkBullet.BulletOwner == "enemy") {
for(var k =0;k&checkBullet.bullets.k++){
var childBullet = checkBullet.bullets[k];
if (this._collide(playerX, playerY, childBullet.x, childBullet.y, 50) == true) {
layer.removeChild(childBullet);
checkBullet.bullets.splice(k,1);
//var blinkAction = cc.blink(1,3);
//scene.plane.runAction(blinkAction);
scene.plane.showHitEffect(childBullet.harm);
if(checkBullet.bullets.length==0){
cc.pool.putInPool(checkBullet);
layer.bullets.splice(i, 1);
for(var s = 0;s&scene.enemies.s++){
//trace("s:"+s);
var checkEnemy = scene.enemies[s];
var enemyX = checkEnemy.x;
var enemyY = checkEnemy.y;
for(var i =0;i&bullets.i++) {
var checkBullet = bullets[i];
if (checkBullet.BulletOwner == "player") {
for(var k =0;k&checkBullet.bullets.k++){
var childBullet = checkBullet.bullets[k];
var bulletX = childBullet.x;
var bulletY = childBullet.y;
if (this._collide(enemyX, enemyY, childBullet.x, childBullet.y, checkEnemy.threshold) == true) {
layer.removeChild(childBullet);
checkBullet.bullets.splice(k,1);
//var blinkAction = cc.blink(1,3);
//scene.plane.runAction(blinkAction);
checkEnemy.showHitEffect(childBullet.harm, {x:bulletX, y:bulletY});
if(checkBullet.bullets.length==0){
cc.pool.putInPool(checkBullet);
layer.bullets.splice(i, 1);
// place out of inside loop, otherwise will case s++ run multiple times!
trace(checkEnemy.blood);
if(checkEnemy.blood&=0){
checkEnemy.explode();
cc.game.enemyManager.removeEnemy(scene, layer, checkEnemy, s);
//check ennemy done
_collide:function(x1, y1, x2, y2, threshold){
//用勾股定理计算举例,对比threshold判定碰撞
if((Math.pow((x1-x2),2)+Math.pow((y1-y2),2))&Math.pow(threshold, 2))
碰撞了的子弹就会被回收,还有一种情况需要回收:没发生碰撞但是飞出了可视窗口。
这种情况在BulletManager里面进行了每帧检测和处理:
manageBullet:function(layer){
if(layer.bullets.length&0)
for(var i=0;i&layer.bullets.i++){
var bullet = layer.bullets[i];
if(bullet._validate()==true)
cc.pool.putInPool(bullet);
layer.bullets.splice(i,1);
6. 物品(Item)系统
物品系统是飞行射击游戏一个常见的系统,具体来说就是可以控制飞机去吃一些随机生成的物品,从而获得一些额外的增强效果。
笔者同样给游戏添加了这个系统。
ItemManager
var ItemManager = cc.Class.extend({
scene:null,
layer:null,
plane:null,
missile:"res/items/missile.png",
//物品以及对于图片
blood:"res/items/blood.png",
shield:"res/items/shield.png",
bigBang:"res/items/bigBang.png",
shine:"res/items/shine.png",
ultra:"res/items/ultra.png"
itemName:["missile", "blood", "shield", "bigBang", "shine", "ultra"],
basicGap: 2,
randomGap: 3,
allowAddItem:true,
floatingItems:[],
privateSprite:null,
durableEffectFunc:[],
allowDurableEffect:true,
ctor:function(){
//ItemManager初始化
this.privateSprite = new cc.Sprite();
this.durableEffectFunc = []; // remove durable effect when re-init game
this.floatingItems = [];
this.allowDurableEffect =
this.allowAddItem =
添加物品其实和添加敌人的做法是类似的,就是在一个随机的位置上添加一个物品,并让它飞行。与敌人不同之处在于,物品只可能与飞机发生碰撞,既不会与敌人也不会与子弹发生碰撞。
generateItem:function(scene, layer, plane){
if(this.allowAddItem == false)
if(this.allowAddItem == true) {
var item = this.itemName[new Date().getTime() % this.itemName.length];
//随机产生物品
//item = this.itemName[1];
var itemSprite = new cc.Sprite(this.items[item]);
itemSprite.itemName =
itemSprite.scale = 0.3;
itemSprite.x = Math.round(Math.random() * cc.game.winSize.width);
itemSprite.y = cc.game.winSize.height + 100;
var moveAction = cc.moveTo(6, cc.p(Math.random() * cc.game.winSize.width, -400));
itemSprite.runAction(moveAction);
//移动物品
layer.addChild(itemSprite, 5);
scene.items.push(itemSprite);
this.allowAddItem =
scene.scheduleOnce(function(){
//物品产生的间隔为基础间隔+随机时间
this.allowAddItem =
}.bind(this), Math.random()*this.randomGap+this.basicGap);
检测物品碰撞
_collide:function(x1, y1, x2, y2, threshold){
if((Math.pow((x1-x2),2)+Math.pow((y1-y2),2))&Math.pow(threshold, 2))
collisionCheck:function(scene, layer, plane){
var itemList = scene.
for(var i=0;i&itemList.i++){
var checkItem = itemList[i];
if(this._collide(checkItem.x, checkItem.y, plane.x, plane.y, 50) == true){
this.takeEffect(scene, checkItem.itemName, plane);
//如果物品与飞机发生了碰撞,那么就应用物品的效果
layer.removeChild(checkItem);
scene.items.splice(i, 1);
物品效果分两种:即时效果和延时效果,前者有加血、一次性范围攻击等,后者有护罩、增强型子弹等。
takeEffect:function(scene, itemName, plane){
switch(itemName){
//根据物品的名称选择要加的效果
case "blood": cc.game.itemManager.addPlaneEffect(scene, "blood", "res/particle/wsparticle_revival01.ccbi", plane, 2);
case "shield":cc.game.itemManager.addPlaneEffect(scene, "shield", "res/particle/wsparticle_buff01.ccbi", plane, 10);
case "missile":cc.game.itemManager.addPlaneEffect(scene, "missile","res/particle/wsparticle_tailinga.ccbi" , plane, 10);
case "bigBang":cc.game.itemManager.addPlaneEffect(scene, "bigBang","res/particle/wsparticle_item_boom_02.ccbi" , plane, 10);
case "shine":cc.game.itemManager.addPlaneEffect(scene, "shine","res/particle/wsparticle_universallylight2.ccbi" , plane, 10);
case "ultra":cc.game.itemManager.addPlaneEffect(scene, "ultra","res/particle/wsparticle_super02.ccbi" , plane, 10);
addPlaneEffect:function(scene, effectName, effectCCBI, plane, duration){
var timeStamp = new Date().getTime();
var locationFunc =
var followTarget =
switch (effectName){
//根据效果的名称决定效果特效的起始位置
case "bigBang":locationFunc = function(x, y){
//bigBang的特效位置是固定的
x:cc.game.winSize.width/2,
y:cc.game.winSize.height/2
};followTarget =
default : locationFunc = function(x, y){
//其余的与飞机当前位置相关
};followTarget =
//followTarget 为true就以为则特效药跟随飞机
var ccbNode = cc.game.particleManager.showParticle(effectName, scene, plane, locationFunc,followTarget, duration);
//使用粒子系统来展示物品的应用特效
if(followTarget == true){
//需要跟随的效果加入到飞机的addedEffects数组中
plane.addedEffects.push({name:effectName, effect:ccbNode, timeStamp:timeStamp});
scene.scheduleOnce(function(){
//this.removeChild(ccbNode);
this.plane.removeAddedEffect(timeStamp);
}.bind(scene), duration);
this.instantEffect(scene, effectName, plane);
//即时效果和延时效果使用不同的处理
this.durableEffect(scene, effectName, plane);
instantEffect:function(scene, effectName,plane){
//即时效果
switch (effectName){
case "bigBang" : this.bigBang(scene, effectName,plane);
case "blood" : this.addBlood(scene, effectName,plane);
durableEffect:function(scene, effectName,plane){
//延时效果
switch (effectName){
case "shine" : this.shineField(scene, effectName,plane);
//shine效果需要添加每帧处理方法
shineField:function(scene, effectName,plane){
//对于有些效果,除了需要添加到飞机的addedEffects上,还需要给出一个效果function来在每帧执行引用效果,例如shine特效,会计算飞机周围一定距离的敌人,并给予一定的伤害
this.durableEffectFunc.push({
effectName:"shine",
effectFunc:function (scene, layer, plane) {
var enemyList = scene.
for (var i = 0; i & enemyList. i++) {
var checkEnemy = enemyList[i];
if (((Math.pow((plane.x-checkEnemy.x),2)+Math.pow((plane.y-checkEnemy.y),2))&Math.pow(300, 2)) == true) {
checkEnemy.showHitEffect(10);
//checkEnemy.blood -= 10;
//if (checkEnemy.blood & 0)
// checkEnemy.blood = 0;
scene.scheduleOnce(function(){
for(var j =0 ;j&this.durableEffectFunc.j++){
if(this.durableEffectFunc[j].effectName == "shine"){
this.durableEffectFunc.splice(j, 1);
}.bind(this), 10);
7. 特效系统
特效系统负责产生并管理两种特效:Particle System和帧动画。
Particle System是设定一些规则,使用纹理图片随机产生一些粒子特效,这些效果是随机的,不可重复的;
帧动画是使用一些图片连续播放产生一些特效效果,这些特效每一次播放都是相同的。
本游戏中,飞机被击中时产生的火花效果是Particle System产生的,而物品效果,Boss的激光以及机体爆炸则是帧动画。
Particle Manager的作用:
在给定的位置上显示特效
回收结束的特效
Particle Manager中有3个show方法,分别处理3种特效:
showParticle:物品特效
showFire:机体被击中的特效
showLaser:Boss的激光射击特效
在需要显示特效的时候,例如子弹碰撞检测判定为击中、敌人血量低于0要爆炸、Boss要发射激光攻击、飞机吃到物品等等,就会调用Particle Manager相应的方法来显示特效。
Particle System
产生Particle System特效的方法:读取plist文件
Particle Manager:
hitEffect = new cc.ParticleSystem("res/particle2/particle.plist");
hitEffect.duration = -1;
hitEffect.setAutoRemoveOnFinish(true);
hitEffect.x = plane.x;
hitEffect.y = plane.y;
plane.gameScene.addChild(hitEffect);
plane.hitEffect = hitE
plane.scheduleOnce(function(){
plane.hitEffect =
//plane.gameScene.removeChild(hitEffect);
hitEffect.x = -1000;
hitEffect.y = -1000;
this.fireEffect.push(hitEffect);
}.bind(this), 1);
想要制作自定义的特效,可以去这个网站制作:
对于本游戏的帧动画,有两个存储格式:plist,ccbi。
其中ccbi文件是cocos2dx所支持的特效文件,在C++和Lua使用广泛,但是JS中暂时找不到使用sample,在官方文档中也找不到相应的API,在stackoverflow中也没有相应的解答。
但是笔者在cocos2dx-js的最新版本的源码中找到了ccbi相关的模块,所以最终是通过阅读这个模块的源码来得知cocos2dx-js是如何读取ccbi文件的,希望可以给读者一些参考。
Particle Manager:
particleList:{
hit:"/res/particle/wsparticle_hit_01.ccbi",
explode:"/res/particle/wsparticle_hit_02.ccbi",
missile:"res/particle/wsparticle_tailinga.ccbi",
blood:"res/particle/wsparticle_revival01.ccbi",
shield: "res/particle/wsparticle_buff01.ccbi",
bigBang: "res/particle/wsparticle_item_boom_02.ccbi",
shine: "res/particle/wsparticle_universallylight2.ccbi",
ultra:"res/particle/wsparticle_super02.ccbi",
bomb:"/res/particle/wsparticle_hit_03.ccbi",
warning:"/res/particle/wsparticle_warning.ccbi"
var nodeLibrary = new cc.NodeLoaderLibrary();
nodeLibrary.registerDefaultCCNodeLoaders();
reader =new cc.BuilderReader(new cc.BuilderReader(nodeLibrary,null, null,null));
var ccbNode = reader.readNodeGraphFromFile(this.particleList[particleName], null, null, new cc.BuilderAnimationManager());
var location = locationFunc(target.x, target.y);
ccbNode.x = location.x;
ccbNode.y = location.y;
scene.addChild(ccbNode);
reader.ccbNode = ccbN
reader.ccbNode.scheduleOnce(function(){
this.readerCache[particleName].push(reader);
//reader.ccbNode.stopAllActions();
if(particleName == "missile") {
// for missile particle re-show bug
reader.ccbNode.x = -400;
reader.ccbNode.y = -400;
scene.removeChild(reader.ccbNode);
}.bind(this), endurance);
8. Stage系统
所谓stage系统,其实就是指游戏的阶段管理。
众所周知,一个游戏一般不会一上来就是打Boss,也不会一上来就是最高难度,会有一个阶段的推进。
所以笔者设计了Stage系统来控制游戏的阶段过渡。
Stage Manager
var StageManager = cc.Class.extend({
stageList:null,
nextStage:true,
stageFunc:null,
preStageFunc:null,
ctor:function(stageList){
//初始化,stageList包含游戏的阶段信息,stageManager将会使用这些信息来切换stage
this.stageList = stageL
this.nextStage =
this.stage = -1;
this.stageFunc={};
this.preStageFunc = {};
this.stageFunc["normal"] = this.normalS
this.stageFunc["mega"] = this.megaS
this.stageFunc["boss"] = this.bossS
this.preStageFunc["boss"] = this.preBossS
manageGameStage:function(scene, layer, plane){
//stage切换方法,每个stage都有自己的执行方法,主要是调整了一些运行配置,诸如敌人产生间隔等
if(this.nextStage == true){
//nextStage 为true时切换至下一个stage
if(this.stage&this.stageList.length-1){
this.stage++;
//console.log("Change stage to:"+this.stageList[this.stage].name);
if(this.preStageFunc[this.stageList[this.stage].name] != null)
this.preStageFunc[this.stageList[this.stage].name](scene, layer, plane);
this.stageFunc[this.stageList[this.stage].name](scene, layer, plane);
this.nextStage =
scene.scheduleOnce(function(){
this.nextStage =
}.bind(this), this.stageList[this.stage].duration);
this.endStage(scene);
this.stageFunc[this.stageList[this.stage].name](scene, layer, plane);
endStage:function(scene){
//默认的结束stage,游戏结束
//console.log("End stage.");
//cc.director.pause();
new GameOverLayer(scene);
normalStage:function(scene, layer, plane){
//常规阶段
scene.plane._shot();
cc.game.enemyManager.addEnemy(scene, layer, 0.2);
cc.game.enemyManager.enemyAttack(scene);
cc.game.bulletManager.manageBullet(layer);
cc.game.bulletManager.collisionCheck(scene, layer);
cc.game.bulletManager.updateBulletLocation(scene, layer, plane);
cc.game.enemyManager.collisionCheck(scene, layer, plane);
cc.game.enemyManager.outEnemyCheck(scene, layer);
scene.plane.update();
scene.gameInfo.update();
if(scene.boss!=null)
scene.boss.update();
if(scene.laserCheck == true)
scene.boss.laserCheck(scene, layer, plane);
cc.game.itemManager.generateItem(scene, layer, plane);
cc.game.itemManager.collisionCheck(scene, layer, plane);
cc.game.itemManager.applyDurableEffect(scene, layer, plane);
if(plane.blood&=0) {
trace("dead");
//cc.director.pause();
cc.game.stageManager.nextStage =
megaStage:function(scene, layer, plane){
//mega阶段
cc.game.enemyManager.enemyFrequency = 1;
cc.game.stageManager.normalStage(scene, layer, plane);
preBossStage:function(scene, layer, plane){
//preBoss阶段,Boss出来前显示一个警告
cc.game.enemyManager.addBoss(scene, layer);
//添加一个Boss
cc.game.enemyManager.enemyFrequency = 3;
cc.game.particleManager.showParticle("warning", scene, plane, function(x, y){
x:cc.game.winSize.width/2,
y:cc.game.winSize.height/2
},false, 4);
bossStage:function(scene, layer, plane){
//boss阶段
cc.game.stageManager.normalStage(scene, layer, plane);
Stage 配置
在main.js里面,启动游戏之前使用stageList配置来配置游戏的stage。
var defaultStage = [{name:"normal", duration: 20}, {name:"mega", duration: 20}, {name:"boss", duration:100000}];
//20秒normal stage,20秒mega stage,然后一直是Boss stage
cc.game.onStart = function(){
cc.view.adjustViewPort(false);
cc.view.setDesignResolutionSize(512, 768, cc.ResolutionPolicy.SHOW_ALL);
cc.view.resizeWithBrowserSize(true);
//load resources
cc.LoaderScene.preload(g_resources, function () {
cc.game.musicManager = new MusicManager();
cc.game.bulletManager = new BulletManager();
cc.game.enemyManager = new EnemyManager();
cc.game.itemManager = new ItemManager();
cc.game.particleManager = new ParticleManager();
cc.game.stageManager = new StageManager(defaultStage);
cc.director.runScene(new MenuScene());
cc.director.setDisplayStats(false);
cc.game.run();
var trace = function() {
//cc.log(Array.prototype.join.call(arguments, ", "));
九、 Android/iOS 原生App
Cocos2dx-JS最大的特点就是跨平台,既可以以html的web形式发布,也可以编译成Android和iOS的原生App发行。但是实际上,要想在移动平台获得web的运行效果,其中有大量的调优和适配工作。有兴趣的同学可以自己研究一下。
至此,笔者的小游戏就大致介绍完了,更加具体的细节,需要读者在学习了Cocos2dx-JS之后再参考源码。本文只是一个大致思路的说明。
笔者学习Cocos2dx-JS纯属偶然,更直接的原因其实是在亚马逊上看到了相关的教程,于是就买了,看了,然后就写了。这其中自然会遇到一些坑,但是在学习新知识的过程中,在不断思考不断改进中收获成长却也是学习的最大乐趣。
这篇简单的教程是自己的小小的总结,如果能帮到大家或者让大家看到后能产生一点点学习新知识的兴趣,也就足够了。
《计算机游戏开发》期末课题实验报告 北京邮电大学数字媒体与设计艺术学院 日 一、游戏简介: 《梦游大逃杀》是一款基于UNITY3D开发制作的第三人称视角射击生存类游戏,其游戏背景是一个画风非常卡通的小朋友,在梦游过程中拿着假想的枪支和自己的毛绒玩具们展开了...
《计算机游戏开发》 期末课题实验报告 姓名:李基铭 班级: 学号: 指导教师:李学明 北京邮电大学数字媒体与设计艺术学院 一、游戏简介: 《梦游大逃杀》是一款基于UNITY3D开发制作的第三人称视角射击生存类游戏,其游戏背景是一个画风...
This article is a record of my journey to learn Game Development and it will keep updating. 由于这篇文章的长度早已超出了简书编辑器的限制,因此后续内容将不在本文中更新,请移步本人的独...
作者:Mandarava(鳗驼螺) 微博:@鳗驼螺pro MV中的小游戏可以用事件来做,也可以用插件来写。本文将使用插件的方式编写一个类似于坦克大战的游戏。本文的目的并非展示如何制作一个完整的坦克大战游戏,而是学习如何使用MV的插件来编写小游戏。所以本文的重点是介绍制作MV...
############
疯狂iOS讲义 李刚 读书笔记
############### 注:1》cocos2d-iphone是由OC语言编写的,cocos2d-X是由C++语言编写的(多平台平移),两者除语言外,基本差别不大。
2》Dash工具(集API文档浏览和代...
鲁大师硬件参数 鲁大师并不能完整的识别电脑信息,甚至屏幕信息显示为27.2寸。 CPU信息 I7-6700K,六代skylake架构,LGA封装。四核八线程,支持Intel睿频加速技术,主频4.0GHz最大睿频4.0-4.2GHz,L3缓存8MB。14nm制程,热设计功耗9...
男性比妻子年龄小三四岁的被访者,对伴侣的满意度达到峰值。 一,你最美丽的青春年华,不是为了救赎一个犯错的男人,也不是为了等待浪子回头。最美的年纪,就应该做最美丽的事情。和好男人谈恋爱,被他们呵护宠爱,永远不用担心第二天他会不会睡在别的女人的床上。 女人会为了爱而出轨,男人...
1.概述 其实这个不是我目前项目里面需要的, 昨晚睡觉之前突然想到实现以下应该不错, 虽然暂时没什么卵用, 但多几个卵也是一种战略储备. 目前这种设计在知乎等这种阅读类的APP里面用的比较多, 或者新闻购物类的, 总之就是可以提供沉浸式阅读体验或者沉浸式购物体验啥的, 这些...
电视背景墙主要是指客厅、卧室里面能反映装修风格的一面主墙,一般电视摆放的位置也是这个屋子里面的视觉中心,最具特色的一个地方,是进门后的视觉焦点。轻装修,重装饰,在这样的愿求下墙体彩绘出现了,深受80后年轻一代的追捧。 一种颜色、一种形状,就能缔造一种简约格调,胜过各种繁复的...

我要回帖

更多关于 塔防类游戏排行榜 的文章

 

随机推荐