设为首页 收藏本站
查看: 1759|回复: 0

用cocos2d-x做一个简单的windows phone 7游戏(一)

[复制链接]

尚未签到

发表于 2015-5-10 07:12:38 | 显示全部楼层 |阅读模式
  
  本教程基于子龙山人翻译的cocos2d的IPHONE教程,用cocos2d-x for XNA引擎重写,加上我一些加工制作。教程中大多数文字图片都是原作者和翻译作者子龙山人,还有不少是我自己的理解和加工。感谢原作者的教程和子龙山人的翻译。本教程仅供学习交流之用,切勿进行商业传播。
  子龙山人翻译的Iphone教程地址:http://www.iyunv.com/andyque/archive/2011/03/22/1990716.html
  Iphone教程原文地址:http://www.raywenderlich.com/352/how-to-make-a-simple-iphone-game-with-cocos2d-tutorial
  游戏截图:
  
DSC0000.png
  
  cocos2d-x for XNA 是在今年2月17号发布的开源引擎,原来这个引擎就有好几个平台的。是一个跨平台的开源引擎。现在很多公司都基于这个引擎来开发游戏。详细可以到http://cn.cocos2d-x.org/了解。它可以为你开发window phone上面的游戏节省大量的时间。目前支持精灵(sprite)、动画、物理引擎、声音引擎以及许许多多非常酷的图像效果等等。
  我也是刚开始学这个引擎,我觉得一个非常简单,但是可以跑起来的游戏。这个游戏包括怎么使用动画、碰撞检测和播放声音,这就够了,并不需要使用太多高级的特性。这样的游戏会对初学者有帮助。
  有人会说为什么要用引擎了。原来XNA就能制作游戏,我也尝试过,但是对于精灵的管理,碰撞的实现,动作的实现,原来的XNA就没那么方便快捷的实现了。有了引擎,很多就不用自己去实现,做起来就快了。
  
  这篇教程将会从头至尾、一步一步地教你如何使用cocos2d-x来制作一个简单的WP7游戏。你可以按照教程一步步来,或者干脆直接跳到文章的最后,下载样例程序。没错!游戏里面有忍者。

  
  
  下载并安装cocos2d-x:
  确认安装了WP7 SDK。
  到http://www.cocos2d-x.org/projects/cocos2d-x/wiki/Download下载FOR WP7,XNA版本,然后,解压安装。这个不用多说。
  Hello, Cocos2D-X!

  让我们从最简单的HelloWorld项目开始吧!启动VS2010。新建项目
  
DSC0001.png
  
  命名为cocos2dHelloWorld。确定。然后有这么一个弹窗:
  
DSC0002.png
  
  这个是openlive提供的一个插件,使游戏可以获取到排名积分等东西。这里我们不需要。去掉那个勾。然后点击OK。这样,项目就算是建好了。游戏的结构不多说。里面包含了两个项目,熟悉XNA游戏的应该了解。不熟悉的这里也不多说,慢慢的会了解的。
  点击运行,发现了很多错误,因为cocos2d的库文件没有找到。
  在cocos2dHelloWorld工程上右键--添加---新建文件夹。命名为lib。然后到安装包下找到这四个文件夹下的bin目录下的对应文件名的DLL。
  
DSC0003.png
  
  添加到项目的lib目录下:
   DSC0004.png
  然后到把引用里面的带叹号的移除,并且添加当前工程目录的DLL。
  
DSC0005.png
  
DSC0006.png
  
  对下面的Content项目也做同样的操作。
  然后点击运行。
DSC0007.png
  
  运行成功!。
  cocos2d-x是按照“场景”(scene)的概念组织的,对一个游戏来说,就好像某个关卡或者屏幕之类的。比如,你可能需要一个场景来为你的游戏建立初使化菜单界面,另外一个场景当作玩游戏的主要界面,还有一个游戏结束的时候的界面。在一个场景里面,你可以有许多“层”(layer)(这个和photoshop有点类似)。每一个层又可以包含一些结点,比如精灵、标签、菜单等。而且一个结点也可以包含其它的结点。(比如,一个精灵可以包含一个子精灵),如果你看一下样例工程,你会看到只有一个场景HelloWorldScene

  现在,把解决方案和项目的名字分别改成cocos2dSimpleGame,cocos2dSimpleGame,cocos2dSimpleGameContent。
  
  新建一个场景。并且添加一个层。
  在cocos2dSimpleGame工程的classes目录上右键--添加---新建项。添加一个类。命名为GamePlayScreen.cs。
  

修改代码为:


namespace cocos2dSimpleGame.Classes
{
class GamePlayScene:CCScene
{
public override void onEnter()
{
base.onEnter();
CCLayerColor colorLayer = CCLayerColor.layerWithColor(new ccColor4B(255, 255, 255, 255));//新建一个白色的颜色层,作为背景
this.addChild(colorLayer);
this.addChild(GamePlayLayer.node());
}
}
class GamePlayLayer:CCLayer
{
public override bool init()
{
if (!base.init())
{
return false;
}
return true;
}
public static new CCLayer node()
{
GamePlayLayer screen = new GamePlayLayer();
if (screen.init())
{
return screen;
}
else
{
screen = null;
}
return screen;
}
}
}
  上面的操作,新建了一个场景,在场景进入的时候,新建了一个白色层当中背景,并且把游戏层添加进去。游戏层中什么操作也没有做。

下面的操作,我们需要一些图片。http://dl.dbank.com/c0l9kbdian

把图片全部添加到Content项目的Images文件夹中。
我觉得还是先需要一个菜单。那么先新建一个菜单类吧。新建一个类命名为MainMenu.cs。并且使MainMenu这个类继承于CCLayer.
在MainMenu类中重载init方法。并且在里面实现背景,Title,菜单的添加。并且在下面实现菜单功能的实现。
具体代码如下:


public override bool init()
{
if (!base.init())
{
return false;
}
CCDirector.sharedDirector().deviceOrientation = ccDeviceOrientation.CCDeviceOrientationLandscapeLeft;
this.m_bIsTouchEnabled = true;//添加触摸
var width = CCDirector.sharedDirector().getWinSize().width;
var height = CCDirector.sharedDirector().getWinSize().height;
CCSprite background = CCSprite.spriteWithFile(@"images/sprites");//加入一个背景
background.position = new CCPoint(width / 2, height / 2);
this.addChild(background);
CCLabelTTF title = CCLabelTTF.labelWithString("Simple Game", "Arial", 24);
title.Color = new ccColor3B(0, 255, 255);
title.position = new CCPoint(width / 2, height - 50);
this.addChild(title);
//添加菜单。。。
CCMenuItemImage playItem = CCMenuItemImage.itemFromNormalImage(@"images/playButton", @"images/playButton", this, playCallback);
CCMenuItemImage aboutItem = CCMenuItemImage.itemFromNormalImage(@"images/aboutButton", @"images/aboutbutton", this, aboutCallback);
CCMenu mainMenu = CCMenu.menuWithItems(playItem, aboutItem);
mainMenu.alignItemsVerticallyWithPadding(15f);
mainMenu.position = new CCPoint(width / 2, height-150);
this.addChild(mainMenu);
CCMenuItemImage exitItem = CCMenuItemImage.itemFromNormalImage(@"CloseSelected", @"CloseSelected", this, exitCallback);
CCMenu exitMenu = CCMenu.menuWithItems(exitItem);
exitMenu.position = new CCPoint(width - exitItem.contentSize.width / 2, exitItem.contentSize.height / 2);
this.addChild(exitMenu);

return true;
}
public static new CCLayer node()
{
MainMenu ret = new MainMenu();
if (ret.init())
{
return ret;
}
else
{
ret = null;
}
return ret;
}
void playCallback(object sender)
{
GamePlayScene pScene = new GamePlayScene();
CCDirector.sharedDirector().pushScene(pScene);
}
void aboutCallback(object sender)
{
}
void exitCallback(object sender)
{
CCDirector.sharedDirector().end();
CCApplication.sharedApplication().Game.Exit();
}
  


注意:在cocos2d,坐标是左下角为(0,0)。右上角为最大。并且精灵等元素的Position都是其中心点。并不是常规的左上角或者其他。

接着修改AppDelegate.cs。看到这类的Launching。在return之前的修改为:


//CCScene pScene = cocos2dSimpleGameScene.scene();
CCScene pScene = CCScene.node();
pScene.addChild(cocos2dSimpleGame.Classes.MainMenu.node());
//run
pDirector.runWithScene(pScene);
  现在,运行后点击Play,就能看到一片白色背景,毕竟我们也没有添加任何东西进去。接下来,添加一个忍者到GameplayLayer。

在GamePlayLayer的init函数return前添加:


            CCDirector.sharedDirector().deviceOrientation = ccDeviceOrientation.CCDeviceOrientationLandscapeLeft;
this.m_bIsTouchEnabled = true;
var screenWidth = CCDirector.sharedDirector().getWinSize().width;
var screenHeight = CCDirector.sharedDirector().getWinSize().height;

CCSprite player = CCSprite.spriteWithFile(@"images/player");
player.position = new CCPoint(player.contentSize.width/2, screenHeight/2);
this.addChild(player);
  编译运行,点击Play,这样忍者就能在白色背景上了。

DSC0008.png

移动目标
  接下来,我们想增加一些目标怪物来与我们的忍者战斗。为了使事情变得更加有趣,我想让这些目标可以移动--实际上这也并不是很难!因此,让我们先在屏幕的右边靠外一点点创建一些目标,然后设置一个action,并使之从右边移动到左边。

接下来,添加一个函数,:


private void addTarget()
{
Random random = new Random();
var screenWidth = CCDirector.sharedDirector().getWinSize().width;
var screenHeight = CCDirector.sharedDirector().getWinSize().height;
CCSprite target = CCSprite.spriteWithFile(@"images/Target");
var minY = target.contentSize.height / 2;
var maxY = screenHeight - target.contentSize.height / 2;
float rangeY = maxY - minY;
float actualY = (random.Next() % rangeY) + minY;
//create the target slightly off-screen along the right edge;
//and along a random position along the Y axis as calculated above
target.position = new CCPoint(screenWidth + screenWidth / 2, actualY);
this.addChild(target);
//Determine speed of the target
float minDuration = 2.0f;
float maxDuration = 4.0f;
float rangeDuration = maxDuration - minDuration;
float actualDuration = random.Next() % rangeDuration + minDuration;
//Create the actions
var actionMove = CCMoveTo.actionWithDuration(actualDuration, new CCPoint(-target.contentSize.width / 2, actualY));
var actionMoveDone = CCCallFuncN.actionWithTarget(this, spriteMoveFinished);
target.runAction(CCSequence.actions(actionMove, actionMoveDone));
}
  在这里我将以一种非常啰嗦的形式来介绍,目的是方便大家理解。第一部分需要解释的是我们之前已经讨论过了的:我们做一些简单的计算来决定把对象放在什么位置,然后设置对象的position,然后并把它加在场景上面,就和加载player精灵一样。
  这里增加的新的元素就是actions。cocos2d里面提供了许多非常方便的内置的action,你可以使用这样action来让你的精灵动起来。比如move action,jump action,fade action,animation action(就是播放图片序列)等等。这里,我们对目标对象使用了3种类型的action:


  • CCMoveTo:我们使用CCMoveTo action让目标从屏幕右边一直往左移动,直到移出屏幕。注意,这里我们可以指定这个过程要花费多长时间。这里使用了变化的时间间隔2-4秒。
  • CCCallFuncN:它可以让你为某个执行此action的对象指定一个回调函数。我们指定的回调函数是:spriteMoveFinished---目前并没有,到后面会具体给了来。
  • CCSequence: 它允许我们把一系列的action组成一个action序列,并且这些acton可以按顺序执行。一次执行完所有的action。在上面的例子中,我们让对象首先执行CcMoveTo,等CCMoveTo完成后,马上就会执行CCCallFuncN action。
  接下来, 为CCCallFuncN action增加一个回调函数。你可以在addTarget前面增加下面的代码:



void spriteMoveFinished(object sender)
{
CCSprite sprite = (CCSprite)sender;
this.removeChild(sprite, true);
}
  这个函数的目的是当精灵飞出屏幕之后,需要移除出当前的scene。这个非常重要,这样的话我们就不会因为屏幕外面积累太多没有用到的精灵而造成内存泄漏。注意,其实还有其它更好的方式来解决这个问题,比如使用一组可以重用的精灵等。不过,对于初学者来说,我在这篇教程里,尽量简单化。
  在我们继续之前,还有最后一件事没做。我们需要调用这个方法来创建我们的目标怪物。而且,为了使事情变得更加有趣,我们会随着时间连续不断地发射一些怪物出来。我们可以使用cocos2d的定时scheduler,并指定一个回调函数来完成此功能。一秒钟调用一次回调函数就可以了。因此,在init函数返回之前,我们再加入下面的代码:



this.schedule(gameLogic, 1.0f);
  然后简单的实现一下这个回调函数,如下:



        void gameLogic(float dt)
{
this.addTarget();
}
  


编译运行,并且play,你可以看到怪物在屏幕上面happy地移动了!

DSC0009.png

发射飞盘
  在这里,我们的忍者需要有一些行动了--因此让我们增加一些射击吧!这里有许许多多实现射击的方式,但是在这个游戏里面,我们想让用户触摸一下屏幕,然后飞盘就会从player开始,沿着你触摸的位置发射出来。
  我们使用CCMoveTo action来实现这个功能。但是,为了使用这个功能,我们必须首先来做一些数学题。这是因为,CCMoveTo需要我们为飞盘指定目的地。但是我们又不能使用触摸点,因为触摸点仅仅代表飞盘飞的方向。我们实际上想让子弹超过触摸点,然后飞出屏幕之外去。
  下面这张图解释了这个问题:
DSC00010.png

因此,就像你看到的,在触摸点和player之间有一个小的三角形,由origin点,offx和offy组成。我们只需要画一个更大的三角形,同时使用一样的比率就行了。然后我们就可以根据比例算出飞盘飞出屏幕的位置。

这里,我们实现ccTouchesEnded方法,这是在用户完成一次touch之后调用的,代码如下:



public override void ccTouchesEnded(List touches, CCEvent event_)
{
CCTouch touch = touches.FirstOrDefault();
CCPoint location = touch.locationInView(touch.view());
location = CCDirector.sharedDirector().convertToGL(location);
//set up initial location of projectile
CCSize winSize = CCDirector.sharedDirector().getWinSize();
CCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile");
projectile.position = new CCPoint(20, winSize.height / 2);
//Determine offset of location to projectile
float offX = location.x - projectile.position.x;
float offY = location.y - projectile.position.y;
//Bail out if we are shooting or backwards
if (offX  0)
{
projectilesToDelete.Add(projectile);
}
targetToDelete.Clear();
}
foreach (CCSprite projectile in projectilesToDelete)
{
_projectiles.Remove(projectile);
this.removeChild(projectile, true);
}
projectilesToDelete.Clear();
}
  上面的代码应该非常清楚。我们仅仅通过遍历projectiles和targets数组,为每个projectile和target创建边界矩形,然后使用CGRectIntersectsRect来检测碰撞。如果发现有碰撞了,我们就从场景中移除精灵,同时也把它移除出数组。注意,我们不得不添加一个toDelete数组,因为我们不能在遍历一个数组的时候去删除数组中的对象。当然,还有许多方式可以实现类似的逻辑,我只不过挑选了简单的方法。
  在你真正完成之前,还差最后一件事情。在你的init方法里面调用下面的函数:



this.schedule(updates);
  编译并运行,现在,当你的飞镖和怪物相碰的时候,他们都会消失啦!
  我们离制作一个可以玩的游戏(但是非常简单)的目标已经越来越近了。我们仅仅需要增加一些音效和背景音乐(试想哪个游戏没有声音呢!),再增加一点点简单的逻辑就更好了。cocos2d-x for xna 支持的音效文件为MP3和WMV。并且音效只支持WMV。背景音支持MP3(或许支持WMV,我没测试过).
  现在,先在Content工程新建一个文件夹命名为resource,添加一些音效文件到resource文件夹吧。背景音MP3自己找,命名为backgroundmusic.mp3.音效文件自己找或者到这里下载http://dl.dbank.com/c0yuasxubb

现在先在cocos2dSimpleGame工程上添加lib目录里面的CocosDenshion.dll引用。
然后在GameplayScreen.cs里面添加using CocosDenshion;
在你的init方法里加载背景音乐:


SimpleAudioEngine.sharedEngine().playBackgroundMusic(@"resource/backgroundmusic");
  然后,在你的ccTouchesEnded方法里面添加音效代码:



SimpleAudioEngine.sharedEngine().playEffect(@"resource/pew-pew-lei");
  在,让我们创建一个新的场景,来作为“You Win”或者“You Lose”的标志。右击Classes文件夹,添加一个新的类,命名为GameOverScene.cs,这次,我们直接在场景上添加相关信息。代码如下;



namespace cocos2dSimpleGame.Classes
{
class GameOverScene:CCScene
{
public CCLabelTTF label;
public GameOverScene()
{
}
public GameOverScene(string msg)
{
CCLayerColor colorLayer = CCLayerColor.layerWithColor(new ccColor4B(255, 255, 255, 255));
this.addChild(colorLayer);
CCSize winSize = CCDirector.sharedDirector().getWinSize();
label = CCLabelTTF.labelWithString(msg, "Arial", 32);
label.Color = new ccColor3B(0, 0, 0);
label.position = new CCPoint(winSize.width / 2, winSize.height / 2);
this.addChild(label);
this.runAction(CCSequence.actions(CCDelayTime.actionWithDuration(3), CCCallFunc.actionWithTarget(this, gameOverDone)));
}
void gameOverDone()
{
CCScene pScene = CCScene.node();
pScene.addChild(cocos2dSimpleGame.Classes.MainMenu.node());
CCDirector.sharedDirector().replaceScene(pScene);
}
}
}
  注意,这里只是在屏幕的中间放置了一个label,然后运行了一个action。这个action的作用就是,等待3秒钟,然后调用一个回调函数切换回HelloWorld场景。最后,让我们增加一些基本的游戏逻辑。首先,让我们来追踪player销毁的飞镖projectiles。接下来,在GamePlayLayer类里面增加两个成员变量,如下所示:



  int projectilesDestroyed = 0;
int quitProjectiles = 0;
  在update方法里,增加(销毁的projectile)计数,同时检测游戏胜利的条件。并在targetsToDelete循环里,紧接着_targets.Remove(target);的地方添加如下代码:



                    projectilesDestroyed++;
label.setString("Count:" + projectilesDestroyed.ToString());
if (projectilesDestroyed > 30)
{
GameOverScene pScene = new GameOverScene("YOU WIN");
//pScene.label.setString();
                        CCDirector.sharedDirector().replaceScene(pScene);
}
  最后,让我们这样设计,只要有10个怪物(原来教程的1个真心容易挂)穿过了屏幕左边,你就输了。修改spriteMoveFinished方法,通过在tag==1里面_targets.Remove(sprite);后面添加下面的代码:



quitProjectiles++;
if (quitProjectiles > 10)
{
GameOverScene pScene = new GameOverScene("YOU LOSE");
//pScene.label.setString();
                    CCDirector.sharedDirector().replaceScene(pScene);
}
  继续,编译并运行程序。现在,你的游戏可以实现胜利或者失败的场景了!接下来,我们来添加一个about场景。添加一个新的类,命名为AboutScene.cs,并且使之继承于CCScene。

具体代码如下。也就添加了一个label。


    class AboutScene:CCScene
{
public AboutScene()
{
CCLayerColor colorLayer = CCLayerColor.layerWithColor(new ccColor4B(255, 255, 255, 255));
this.addChild(colorLayer);
string msg = "此程序由fengyun1989基于子龙山人翻译\r\n的IPHONE教程制作,仅供学习交流之用,\r\n切勿进行商业传播。如有什么意见或者建议,\r\n请发邮件到1024919409@qq.com!";
CCLabelTTF label = CCLabelTTF.labelWithString(msg, "yaheiFont", 24);
CCSize winSize = CCDirector.sharedDirector().getWinSize();
label.position = new CCPoint(winSize.width / 2, winSize.height/2 - 40);      
this.addChild(label);
}
}
  


这里有一个问题,就是中文支持的问题。具体操作可以看我转载的文章。里面有详细的操作。按照里面的操作就能完成中文的支持了。《转载》让cocos2d-x for WP7添加中文支持

另外,修改MainMenu的AboutCallBack。


        void aboutCallback(object sender)
{
AboutScene pScene = new AboutScene();
CCDirector.sharedDirector().replaceScene(pScene);
}
  这样,拥有一个菜单,简单的游戏,关于提示窗口就完成了。

但是,如果玩下酒知道有问题,就是无论在哪个场景,只要按返回键,游戏就退出了。。。这样的设定很不友好,我玩游戏的时候想重新玩,还得重新进游戏,而不是按返回键退回到菜单。我这里就写了篇解决方法:http://blog.iyunv.com/fengyun1989/article/details/7468699
那么来操作下吧。
打开Game1.cs,找到Update函数。在里面就能看到处理返回键的操作
因为我们这里有3个场景,分别是GamePlayScene,GameOverScene。AboutScene。
所以修改代码如下:


// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
CCScene pScene = CCDirector.sharedDirector().runningScene;
//Debug.WriteLine(pScene.GetType().ToString());
string currentScene = pScene.GetType().ToString();
if (currentScene == "cocos2dSimpleGame.Classes.GamePlayScene" || currentScene == "cocos2dSimpleGame.Classes.GameOverScene"
|| currentScene == "cocos2dSimpleGame.Classes.AboutScene")
{
CCScene mainMenu = CCScene.node();
mainMenu.addChild(cocos2dSimpleGame.Classes.MainMenu.node());
CCDirector.sharedDirector().replaceScene(mainMenu);
}
else
this.Exit();
}
  


这样,一个不错的游戏就做好了。


DSC00011.png

DSC00012.png

DSC00013.png

工程代码下载:http://dl.dbank.com/c0kvl53qm8
继续学习:用cocos2d-x做一个简单的windows phone 7游戏:旋转炮塔(二)

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-65361-1-1.html 上篇帖子: windows mobile开发循序渐进(7)windows mobile device center连接windows mobile 5.0及简单移动程序开发 下篇帖子: Microsoft Press ebook--Programming Windows Phone 7
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表