91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android游戲開發十日通(7)- 開發一個雙人游戲

發布時間:2020-08-08 15:36:57 來源:網絡 閱讀:601 作者:拳四郎 欄目:開發技術

提要

       游戲需要分享才能獲得快樂,想想你以前玩過的那些游戲,那些會是真正地存在你嬸嬸的腦海里?是獨自一人躲在被窩里酣戰PSP,還是和哥們在網吧一起開黑?是一個人單刷迅龍三連,還是和朋友聯機怒刷黃黑龍?

       從來沒有孤獨的快樂,也從來沒有孤獨的游戲。

       今天要做的就是一個非常簡單但有有點復雜的雙人游戲-Air Hockey。

Android游戲開發十日通(7)- 開發一個雙人游戲

通過這篇教程,你可以學到

1.cocos2d-x的生命周期;

2.如何讓游戲兼容各種屏幕;

3.如何處理多點觸控;

4.如何模擬斜碰...


創建游戲

還是老套路

終端進入 cocos2d-x-2.2/tools/project-creator/ ,執行
./create_project.py -project AirHockey -package com.maclab.airhockey -language cpp
在 /cocos2d-x-2.2/projects/AirHockey  中就有創建好的各平臺的工程模板。

修改proj.android 下build_native.sh,添加一行指定NDK_ROOT

在eclipse中導入proj.android 工程,記得不要勾Copy to Project into workspace.

如果之前未將 /cocos2d-x-2.2/cocos2dx/platform/android/java 導入,在這里要導入。

創建軟鏈接,終端進入 pro.android,執行命令:
ln -s ../Resources ./Resources
在Eclipse中刷新工程,Resources文件夾就出現了。

換一個狂拽酷炫點的圖標

Android游戲開發十日通(7)- 開發一個雙人游戲

將android工程中res文件夾下的icon.png換成這個就可以了。

在項目上右擊,run as->android application
一切順利的話工程就創建好了,如果沒法運行,檢查cocos2dx是否配置好。


cocos2d-x的生命周期

       這個問題上一篇教程直接略過了,這里來仔細分析下。

       AirHockey文件夾下有一個Classes文件夾和幾個pro.*文件夾,Classes文件夾有兩個類,一個AppDelegate,似私有繼承自cocos2d::CCApplication,一個HelloWorldScence類,共有繼承自cocos2d::CCLayer。

Android游戲開發十日通(7)- 開發一個雙人游戲

大部分框架,基本上都可以分為兩部分:
1. 一個入口主類,它定義了整個應用程序的生命周期,并提供一些全局的資源
2. 一些繪制到屏幕上的“頁面”控件。


在cocos2d中,CCApplication主要做三件事情:
1. 控制應用程序的生命周期
2. 提供和管理一些全局的資源
3. 處理Touch
4. 循環繪制界面

應用程序的生命周期有一下幾個虛方法:
applicationDidFinishLaunching();資源加載完成之后發生
applicationDidEnterBackground();程序進入后臺被掛起
applicationWillEnterForeground();程序從后臺被喚醒

下面是AppDelegate.cpp的內容

#include "AppDelegate.h" #include "HelloWorldScene.h"  USING_NS_CC;  AppDelegate::AppDelegate() {  }  AppDelegate::~AppDelegate()  { }  bool AppDelegate::applicationDidFinishLaunching() {     // 初始化導演類     CCDirector* pDirector = CCDirector::sharedDirector();     CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();      pDirector->setOpenGLView(pEGLView); 	     // 顯示狀態信息     pDirector->setDisplayStats(true);      // 設置FPS. 默認是60fps     pDirector->setAnimationInterval(1.0 / 60);      //創建一個場景. 它會自動釋放內存     CCScene *pScene = HelloWorld::scene();      // 運行     pDirector->runWithScene(pScene);      return true; }  //當程序被掛起的時候被調用,搓大招的時候老婆電話來了也會被調用  This function will be called when the app is inactive. When comes a phone call,it's be invoked too void AppDelegate::applicationDidEnterBackground() {     CCDirector::sharedDirector()->stopAnimation();      // 如果使用了SimpleAudioEngine,下面這行代碼的注釋請去掉     // SimpleAudioEngine::sharedEngine()->pauseBackgroundMusic(); }  //當游戲從掛起狀態回來的時候,方法被調用 void AppDelegate::applicationWillEnterForeground() {     CCDirector::sharedDirector()->startAnimation();      // 如果使用了SimpleAudioEngine,下面這行代碼的注釋請去掉     // SimpleAudioEngine::sharedEngine()->resumeBackgroundMusic(); } 

HelloWorldScence這個類就不多說了,所有游戲的邏輯,顯示...都在這里調用,如果你還不熟悉,參考:Android游戲開發十日通(6)- 太空大戰

下面是看linux和Android中怎樣啟動游戲的。


      首先看pro.linux下的代碼,只有一個main.cpp

#include "../Classes/AppDelegate.h" #include "cocos2d.h" #include "CCEGLView.h"  #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string>  USING_NS_CC;  int main(int argc, char **argv) {     // create the application instance     AppDelegate app;     CCEGLView* eglView = CCEGLView::sharedOpenGLView();     eglView->setFrameSize(800, 480);     return CCApplication::sharedApplication()->run(); }

CCEGLView封裝了使用openGL作為顯示底層API的一個基本的窗體的創建和控制,在linux中,窗口用的是GLFW庫。

main中第一行生命了一個AppDelegate對象,第二行初始化一個CCEGLView指針,sharedOpenGLView中的內容如下:

CCEGLView* CCEGLView::sharedOpenGLView()   {            if (s_pEglView == NULL)//s_pEglView是一個CCEGLView指針,靜態成員變量       {           s_pEglView = new CCEGLView();           if(!s_pEglView->Create())//main中調用時,會執行這一步           {               delete s_pEglView;               s_pEglView = NULL;           }       }          return s_pEglView;   }  

就是new一個對象。第三行設置窗口的大小,

我們再看看CCApplication::sharedApplication()->run()執行的又是什么東西。

int CCApplication::run()   {       PVRFrameEnableControlWindow(false);          // Main message loop:       MSG msg;       LARGE_INTEGER nFreq;       LARGE_INTEGER nLast;       LARGE_INTEGER nNow;          QueryPerformanceFrequency(&nFreq);//獲取當前系統頻率和計數       QueryPerformanceCounter(&nLast);          // Initialize instance and cocos2d.       if (!applicationDidFinishLaunching())//虛函數,調用子類的重載,這里也會設置一些顯示窗口的屬性       {           return 0;       }          CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();獲取CCEGLView的單一實例       pMainWnd->centerWindow();       ShowWindow(pMainWnd->getHWnd(), SW_SHOW);//這里顯示窗口          while (1)//消息循環       {           if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))           {               // 獲取當前的計數               QueryPerformanceCounter(&nNow);                  // 判斷時間流逝,是否該繪制下一幀               if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)               {                   nLast.QuadPart = nNow.QuadPart;                   CCDirector::sharedDirector()->mainLoop();//渲染場景(清除顯示設備,重繪場景)               }               else               {                   Sleep(0);               }               continue;           }              if (WM_QUIT == msg.message)//獲取退出消息,跳出循環           {               // Quit message loop.               break;           }              // 處理Windows消息           if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))           {               TranslateMessage(&msg);               DispatchMessage(&msg);           }       }          return (int) msg.wParam;   }  

 總結一下,做了下面幾件事:

(1)首先先獲取當前系統的頻率和計數。這是一個很大的值,所以用了一個LARGE_INTEGER型變量來存儲。
(2)調用子類的applicationDidFinishLaunching(),執行進入程序后的一些初始化工作。
(3)獲取CCEGLView單例,顯示窗口。
(4)進入循環While(1),重繪每一幀的場景。

感興趣的繼續研究源碼...

可以總結出linux下的啟動流程:

Android游戲開發十日通(7)- 開發一個雙人游戲


再看Android平臺。

Android使用的是java,cocos2d-x使用的C++,這里其實是用C++開發android程序,需要的用到技術是JNI。(不知道JNI為何物的猛擊我)

看一下pro.android/jni/hellocpp/main.cpp

#include "AppDelegate.h" #include "cocos2d.h" #include "CCEventType.h" #include "platform/android/jni/JniHelper.h" #include <jni.h> #include <android/log.h>  #define  LOG_TAG    "main" #define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)  using namespace cocos2d;  extern "C" {      jint JNI_OnLoad(JavaVM *vm, void *reserved) {     JniHelper::setJavaVM(vm);      return JNI_VERSION_1_4; }  void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h) {     if (!CCDirector::sharedDirector()->getOpenGLView())     {         CCEGLView *view = CCEGLView::sharedOpenGLView();         view->setFrameSize(w, h);          AppDelegate *pAppDelegate = new AppDelegate();         CCApplication::sharedApplication()->run();     }     else     {         ccGLInvalidateStateCache();         CCShaderCache::sharedShaderCache()->reloadDefaultShaders();         ccDrawInit();         CCTextureCache::reloadAllTextures();         CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL);         CCDirector::sharedDirector()->setGLDefaultValues();      } }  }

這里主要有兩個函數:

(1)JNI_OnLoad,這個函數主要是用來告訴Android VM當前使用的是什么版本是Jni,如果不提供此函數,則默認使用Jni1.1版本。這個函數在加載交叉編譯的庫的時候,就會執行。
(2)Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit,這個函數很明顯就是運行一個cocos2d-x的應用實例了,這和Win32是一樣的,當然它多了一個openGlView的檢測。一旦調用了它那么cocos2d-x游戲啟動。
接下來再看看它們是在哪里被調用的。


看一下Android工程中的主類:

package com.maclab.airhockey;  import org.cocos2dx.lib.Cocos2dxActivity; import org.cocos2dx.lib.Cocos2dxGLSurfaceView;  import android.os.Bundle;  public class AirHockey extends Cocos2dxActivity{ 	     protected void onCreate(Bundle savedInstanceState){ 		super.onCreate(savedInstanceState);	 	}       public Cocos2dxGLSurfaceView onCreateView() {     	Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);     	// AirHockey should create stencil buffer     	glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8);     	     	return glSurfaceView;     }      static {         System.loadLibrary("cocos2dcpp");     }      } 

cocos2dcpp是由C++交叉變異出來的庫,在這里將它加載進來。這個activity是繼承自Cocos3dxActivity,我們知道android應用的生命周期都是從onCreate開始的,在Cocos3dxActivity的onCreate方法中。調用了一個init函數,內容如下:

public void init() { 		     	// FrameLayout         ViewGroup.LayoutParams framelayout_params =             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,                                        ViewGroup.LayoutParams.FILL_PARENT);         FrameLayout framelayout = new FrameLayout(this);         framelayout.setLayoutParams(framelayout_params);          // Cocos2dxEditText layout         ViewGroup.LayoutParams edittext_layout_params =             new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,                                        ViewGroup.LayoutParams.WRAP_CONTENT);         Cocos2dxEditText edittext = new Cocos2dxEditText(this);         edittext.setLayoutParams(edittext_layout_params);          // ...add to FrameLayout         framelayout.addView(edittext);          // Cocos2dxGLSurfaceView         this.mGLSurfaceView = this.onCreateView();          // ...add to FrameLayout         framelayout.addView(this.mGLSurfaceView);          // Switch to supported OpenGL (ARGB888) mode on emulator         if (isAndroidEmulator())            this.mGLSurfaceView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);          this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());         this.mGLSurfaceView.setCocos2dxEditText(edittext);          // Set framelayout as the content view 		setContentView(framelayout); 	} 	

在這里Cocos2dxActivity做的就是創建Cocos2dxGLSurfaceView,并設置了Cocos2dxRenderer和Cocos2dxEditText,然后添加到FramLayout。

而NativeIint方法在則在Cocos2dxRenderer中被調用:

	@Override 	public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) { 		Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight); 		this.mLastTickInNanoSeconds = System.nanoTime(); 	} 


最后總結層次調用關系如下:

Android游戲開發十日通(7)- 開發一個雙人游戲


是不是有點頭暈~_~,沒關系,別忘了我們最初的目標 - 做狂拽炫酷的游戲!


為游戲支持不同分辨率的屏幕

       要讓游戲在不同分辨率下都獲得良好的用戶體驗,應該滿足這幾個要求:

背景圖填滿整個畫面,不出現黑邊;
背景圖的主要內容都顯示在屏幕上,盡可能少的裁剪圖片(減少超出屏幕可視區域的部分);
如果背景圖需要放大,盡可能減小放大的比例,避免放大后出現明顯的模糊;
用戶界面的文字標簽、按鈕在任何分辨率下都應該完整顯示,并且容易交互。


       相信很多游戲開發者開發者選擇ios作為首選開發平臺的一個主要原因就是IOS沒有坑爹的碎片化,系統沒有碎片化,屏幕沒有碎片化, 機器性能沒有碎片化....但到了Android,一切就成了噩夢。

      一個解決屏幕碎片化的方法就是為不同的屏幕分辨率準備不同的資源文件。另一種方法是按比例縮放。

縮放比例的做法比較方便,在AppDelegate::applicationDidFinishLaunching()中添加代碼:

CCEGLView::sharedOpenGLView()->setDesignResolutionSize(800, 480,kResolutionExactFit);

在這里,設定 超過這個或者小于這個分辨率,cocos2d-x會做自適應處理。
第三個參數是自適應分辨率的規則,有3種
kResolutionExactFit 在指定的應用的現實區域會嘗試去保持原始比例,但是有可能會發生變形,這時候你的應用將會被拉伸或者壓縮
kResolutionNoBorder 在指定的應用文件區域內不會出現變形,但是可以能有一些裁剪
kResolutionShowAll 在指定的應用文件區域內不會出現變形,將保持原樣。但是兩邊會出現黑邊。

在800*480窗口下,正常現實的效果如下:

Android游戲開發十日通(7)- 開發一個雙人游戲



CCEGLView::sharedOpenGLView()->setDesignResolutionSize(200, 480,kResolutionExactFit);

Android游戲開發十日通(7)- 開發一個雙人游戲


CCEGLView::sharedOpenGLView()->setDesignResolutionSize(200, 480,kResolutionShowAll);

Android游戲開發十日通(7)- 開發一個雙人游戲

    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(200, 480,kResolutionNoBorder);

Android游戲開發十日通(7)- 開發一個雙人游戲


如果是采用準備多套資源文件的方法,則在Resources文件夾中創建img/xhdip和img/hdip文件夾。

將下載好的圖片放到對應的文件夾中,

還是修改AppDelegate.cpp的代碼。

首先是添加一個結構體,在定義幾個static變量:

typedef struct tagResource {     cocos2d::CCSize size;     char directory[100]; }Resource;  static Resource smallResource  =  { cocos2d::CCSizeMake(1024, 600),   "img/hdip" }; static Resource mediumResource =  { cocos2d::CCSizeMake(1280, 720),  "img/xhdip" }; static cocos2d::CCSize designResolutionSize = cocos2d::CCSizeMake(1024, 600);   


我這里只定義了small和medium,默認的屏幕是1024*600,因為這是我的平板的屏幕。
然后修改AppDelegate::applicationDidFinishLaunching,在應用啟動之后馬上初始化資源。


bool AppDelegate::applicationDidFinishLaunching() {     // initialize director     CCDirector* pDirector = CCDirector::sharedDirector();     CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();     pDirector->setOpenGLView(pEGLView);  //CCEGLView::sharedOpenGLView()->setDesignResolutionSize(200, 480,kResolutionShowAll);     CCEGLView::sharedOpenGLView()->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, kResolutionNoBorder);           CCSize frameSize = pEGLView->getFrameSize();            std::vector<std::string> resDirOrders;       // if the frame's height is larger than the height of medium resource size, select large resource.     if (frameSize.height > mediumResource.size.height)     {          pDirector->setContentScaleFactor(mediumResource.size.height/designResolutionSize.height);         resDirOrders.push_back(mediumResource.directory);     }          // if the frame's height is larger than the height of small resource size, select medium resource.     else     {          pDirector->setContentScaleFactor(smallResource.size.height/designResolutionSize.height);         resDirOrders.push_back(smallResource.directory);     }     resDirOrders.push_back("sounds");       resDirOrders.push_back("/");     CCFileUtils::sharedFileUtils()->setSearchResolutionsOrder(resDirOrders);      // turn on display FPS    pDirector->setDisplayStats(true);       // set FPS. the default value is 1.0/60 if you don't call this     pDirector->setAnimationInterval(1.0 / 60);       // create a scene. it's an autorelease object        CCScene *pScene = HelloWorld::scene();        // run       pDirector->runWithScene(pScene);     return true;   }



這樣,資源的搜索路徑就被設置好了 -大屏幕設備加載資源時,首先查找Resources/img/xhdip,然后搜Resources;小屏幕設備加載資源時,首先查找Resources/img/hdip,然后搜Resources。


預加載音效

將資源中的*.wav文件都放到工程的Resources文件夾中。

首先修改pro.linux 下的MakeFile,

INCLUDES = -I.. -I../Classes\                        -I$(COCOS_ROOT)/CocosDenshion/include   SHAREDLIBS += -lcocos2d -lcocosdenshion  

在AppDelegate.cpp中添加頭文件和命名空間,

#include "SimpleAudioEngine.h" using namespace CocosDenshion ;

在ApplicationDidFinishLanuching中添加加載的語句,

    //Setting sounds     std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename("hit.wav");     SimpleAudioEngine::sharedEngine()->preloadEffect(fullPath.c_str());     fullPath = CCFileUtils::sharedFileUtils()->fullPathForFilename("score.wav");     SimpleAudioEngine::sharedEngine()->preloadEffect(fullPath.c_str());


創建自己的CCSprite

首先看一下何為Sprite.

        Spirite是渲染框架中最小的一個單元了,游戲精靈是游戲制作中非常重要的一個概念。它是游戲內容最好的呈現。游戲開發者主要的控制對象也會是精靈。精靈對象可以是游戲畫面中的任何一對象,可以是游戲中的主角,也可以是汽車,或者是一個樹,一個高富帥,一個窮屌絲,哪怕是一片樹葉也可以是一個精靈對象。從技術角度來看,精靈對象就是一個可以不斷變化的圖片,而其本身也具備了一些特殊的屬性和方法,比如紋理、尺寸、翻轉、透明以及角度的改變。精靈作為游戲的重要元素,最后也會被開發者新賦予一些新的邏輯或者物理屬性。

下面是我們繼承CCSprite的GameSprite類。

gamesprite.h

#ifndef GAMESPRITE_H #define GAMESPRITE_H #include "cocos2d.h" using namespace cocos2d;  class GameSprite : public CCSprite { public:      CC_SYNTHESIZE(CCPoint, _nextPosition, NextPosition);     CC_SYNTHESIZE(CCPoint, _vector, Vector);     CC_SYNTHESIZE(CCTouch *, _touch, Touch);      GameSprite(void);     ~GameSprite(void);      static GameSprite* gameSpriteWithFile(const char *pszFileName);     virtual void setPosition(const CCPoint& pos);     float radius(){return getTexture()->getContentSize().width * 0.5f;} };  #endif // GAMESPRITE_H 


gamesprite.cpp

#include "gamesprite.h"  GameSprite::GameSprite(void) {     _vector = ccp(0,0); }  GameSprite::~GameSprite(void) { }  GameSprite* GameSprite::gameSpriteWithFile(const char *pszFileName) {     GameSprite * sprite = new GameSprite();     if (sprite && sprite->initWithFile(pszFileName)) {         sprite->autorelease();         return sprite;     }     CC_SAFE_DELETE(sprite);     return NULL; }  void GameSprite::setPosition(const CCPoint& pos) {     CCSprite::setPosition(pos);     if (!_nextPosition.equals(pos)) {         _nextPosition = pos;     } } 


gameSpriteWithFIle用于從文件創建一個自動釋放內存的Sprite;setPosition用于改變Sprite位置,同時更新_nextPosition; radius這個inline函數用于返回物體的半徑,也就是sprite寬度的一半。

修改proj.linux/MakeFile,將新添加的 cpp 索引進來。

SOURCES = main.cpp \           ../Classes/AppDelegate.cpp \           ../Classes/HelloWorldScene.cpp\           ../Classes/gamesprite.cpp  




android版本的編譯需要修改proj.android/jni/Android.mk

LOCAL_SRC_FILES := hellocpp/main.cpp \                    ../../Classes/AppDelegate.cpp \                    ../../Classes/HelloWorldScene.cpp\                     ../../Classes/gamesprite.cpp


搭建場景

場景的搭建都在HelloWorld::init()中處理。

// on "init" you need to initialize your instance bool HelloWorld::init() {      _player1Score = 0;     _player2Score = 0;     _screenSize = CCDirector::sharedDirector()->getWinSize();      //////////////////////////////     // 1. super init first     if ( !CCLayer::init() )     {         return false;     }          CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();     CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();      // add court background     CCSprite* court = CCSprite::create("court.png");      // position court on the center of the screen     court->setPosition(ccp(visibleSize.width* 0.5 + origin.x, visibleSize.height* 0.5 + origin.y));      // add court as a child to this layer     this->addChild(court, 0);      _player1 = GameSprite::gameSpriteWithFile("mallet.png");     _player1->setPosition(ccp(_player1->radius() * 2,_screenSize.height * 0.5) );     this->addChild(_player1,1);     _player2 = GameSprite::gameSpriteWithFile("mallet.png");     _player2->setPosition(ccp(_screenSize.width - _player1->radius() * 2, _screenSize.height * 0.5));     this->addChild(_player2,1);     _ball = GameSprite::gameSpriteWithFile("puck.png");     _ball->setPosition(ccp( _screenSize.width* 0.5 - 2 * _ball->radius(), _screenSize.height * 0.5));     this->addChild(_ball,1);      _players = CCArray::create(_player1, _player2, NULL);     _players->retain();      _player1ScoreLabel = CCLabelTTF::create("0", "Arial", 60);     _player1ScoreLabel->setPosition(ccp( _screenSize.width * 0.5 - 80, _screenSize.height - 60));     this->addChild(_player1ScoreLabel, 2);      _player2ScoreLabel = CCLabelTTF::create("0", "Arial", 60);     _player2ScoreLabel->setPosition(ccp(_screenSize.width * 0.5 + 80, _screenSize.height - 60));     this->addChild(_player2ScoreLabel, 2);      //listen for touches     this->setTouchEnabled(true);     //create main loop     this->schedule(schedule_selector(HelloWorld::update));     return true; }

非常簡單,無非是初始化一些變量,在場景中添加背景,添加Sprite,記分牌。激活觸控,設置更新的回調函數。

運行之后的效果:

Android:、

Android游戲開發十日通(7)- 開發一個雙人游戲

Linux:

Android游戲開發十日通(7)- 開發一個雙人游戲


在析構函數中釋放之前開辟的內存:

HelloWorld::~HelloWorld() {     CC_SAFE_RELEASE(_players); }


動起來

一步步來,首先是處理多點觸控。

在HelloWorld.h中添加三個關于主控的函數:

    virtual void ccTouchesBegan(CCSet* pTouches, CCEvent*event);     virtual void ccTouchesMoved(CCSet* pTouches, CCEvent*event);     virtual void ccTouchesEnded(CCSet* pTouches, CCEvent*event);

網文生意,這三個函數分別在觸控開始,觸控移動,觸控結束時調用。

這里主要要處理的問題就是手指和GamePlayer對應問題,用下面的代碼就可以解決。

    CCSetIterator i;     CCTouch* touch;     CCPoint tap;     GameSprite * player;     for( i = pTouches->begin(); i != pTouches->end(); i++)     {         touch = (CCTouch*) (*i);         if(touch)         {             tap = touch->getLocation();             for (int p = 0; p < 2; p++)             {                 player = (GameSprite *) _players->objectAtIndex(p);                //Do some thing.             }         }     }

前四行聲名了4個局部變量,i是CCSet的迭代器,touch是指向CCTouch的指針,CCPoint用于記錄觸碰的位置,player是一個GameSpirit的指針。

第五行遍歷所有的觸控點,接下來對每個觸控點進行處理。第六行將迭代器轉換為CCTouch指針,接下來對于當前的觸控點,再遍歷所有_players中的player,然后進行處理。

三個關于觸控的函數的處理流程相同,不同的是做的工作不一樣。

ccTouchesBegan中要做的是將觸控點與對應的player連接起來,ccTouchesMoved要做的是更新player的位置,ccTouchesEnded要做的是清空觸控信息。具體的實現如下:

void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *event) {     CCSetIterator i;     CCTouch* touch;     CCPoint tap;     GameSprite * player;     for( i = pTouches->begin(); i != pTouches->end(); i++)     {         touch = (CCTouch*) (*i);         if(touch)         {             tap = touch->getLocation();             for (int p = 0; p < 2; p++)             {                 player = (GameSprite *) _players->objectAtIndex(p);                 if (player->boundingBox().containsPoint(tap))                 {                     player->setTouch(touch);                 }             }         }     } }  void HelloWorld::ccTouchesMoved(CCSet *pTouches, CCEvent *event) {     CCSetIterator i;     CCTouch* touch;     CCPoint tap;     GameSprite * player;     for( i = pTouches->begin(); i != pTouches->end(); i++)     {         touch = (CCTouch*) (*i);         if(touch)         {             tap = touch->getLocation();             for (int p = 0; p < 2; p++)             {                 player = (GameSprite *) _players->objectAtIndex(p);                 if (player->getTouch() != NULL && player->getTouch() == touch)                 {                     CCPoint nextPosition = tap;                     //keep player inside screen                     if (nextPosition.x < player->radius())                         nextPosition.x = player->radius();                     if (nextPosition.x > _screenSize.width - player->radius())                         nextPosition.x = _screenSize.width - player->radius();                     if (nextPosition.y < player->radius())                         nextPosition.y = player->radius();                     if (nextPosition.y > _screenSize.height - player->radius())                         nextPosition.y = _screenSize.height - player->radius();                     //keep player inside its court                     if (player->getPositionX() < _screenSize.width *0.5f)                     {                         if (nextPosition.x > _screenSize.width * 0.5 - player->radius())                         {                             nextPosition.x = _screenSize.width * 0.5 - player->radius();                         }                     } else{                         if (nextPosition.x < _screenSize.width * 0.5 + player->radius())                         {                             nextPosition.x = _screenSize.width * 0.5 + player->radius();                         }                     }                     player->setNextPosition(nextPosition);                     player->setVector(ccp(tap.x - player->getPositionX(), tap.y - player->getPositionY()));                 }             }         }     } }  void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent *event) {     CCSetIterator i;     CCTouch* touch;     CCPoint tap;     GameSprite * player;     for( i = pTouches->begin(); i != pTouches->end(); i++)     {         touch = (CCTouch*) (*i);         if(touch)         {             tap = touch->getLocation();             for (int p = 0; p < 2; p++)             {                 player = (GameSprite *) _players->objectAtIndex(p);                 if (player->getTouch() != NULL && player->getTouch() == touch) {                     player->setTouch(NULL);                     player->setVector(ccp(0,0));                 }             }         }     }  }

編譯,運行,我點~為什么不能動!這是為~什么!

因為根本就沒有在游戲中更新player的位置啊。在HelloWorld::update中添加下面的代碼:

    //move pieces to next position     _player1->setPosition(_player1->getNextPosition());     _player2->setPosition(_player2->getNextPosition());     _ball->setPosition(_ball->getNextPosition());

運行效果:

Android游戲開發十日通(7)- 開發一個雙人游戲


碰撞檢測

這里的碰撞檢測要處理兩個方面,一個是player和小球的碰撞,一個是墻壁和小球的碰撞。player和小球的碰撞會改變小球的運動路線,墻壁和小球碰撞,要么反彈,要么得分。

player和小球碰撞要注意一個“陷入”的問題,就是在檢測的時候要預測一下小球的走位,不然小球和player就會發生重疊。

Android游戲開發十日通(7)- 開發一個雙人游戲

還有就是小球和player發生碰撞的情況屬于斜碰,在計算的時候,首先通過player和小球的速度計算出一個附加在小球上的力,然后再計算出一個角度,最后設置小球的速度。

和墻壁的碰撞就簡單一些了,也要注意陷入的問題。然后就是得分的話要更新記分牌。

最后別忘了在碰撞的時候添加音效。完整的update函數如下:

void HelloWorld::update(float dt) {     CCPoint ballNextPosition = _ball->getNextPosition();     CCPoint ballVector = _ball->getVector();     ballVector = ccpMult(ballVector, 0.98f);     ballNextPosition.x += ballVector.x;     ballNextPosition.y += ballVector.y;      //test for puck and mallet collision     float squared_radii = pow(_player1->radius() + _ball->radius(), 2);      GameSprite * player;     CCPoint playerNextPosition;     CCPoint playerVector;     for (int p = 0; p < 2; p++) {          player = (GameSprite *) _players->objectAtIndex(p);         playerNextPosition = player->getNextPosition();         playerVector = player->getVector();          float diffx = ballNextPosition.x - player->getPositionX();         float diffy = ballNextPosition.y - player->getPositionY();          float distance1 = pow(diffx, 2) + pow(diffy, 2);         float distance2 = pow(_ball->getPositionX() - playerNextPosition.x, 2) + pow(_ball->getPositionY() - playerNextPosition.y, 2);          if (distance1 <= squared_radii || distance2 <= squared_radii) {              float mag_ball = pow(ballVector.x, 2) + pow(ballVector.y, 2);             float mag_player = pow (playerVector.x, 2) + pow (playerVector.y, 2);              float force = sqrt(mag_ball + mag_player);             float angle = atan2(diffy, diffx);              ballVector.x = force * cos(angle);             ballVector.y = force * sin(angle);              ballNextPosition.x = playerNextPosition.x + (player->radius() + _ball->radius() + force) * cos(angle);             ballNextPosition.y = playerNextPosition.y + (player->radius() + _ball->radius() + force) * sin(angle);              SimpleAudioEngine::sharedEngine()->playEffect("score.wav");         }     }      //check collision of ball and sides     if (ballNextPosition.x < _ball->radius()) {         ballNextPosition.x = _ball->radius();         ballVector.x *= -0.8f;         SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");     }      if (ballNextPosition.x > _screenSize.width - _ball->radius()) {         ballNextPosition.x = _screenSize.width - _ball->radius();         ballVector.x *= -0.8f;         SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");     }     //ball and top of the court     if (ballNextPosition.y > _screenSize.height - _ball->radius()) {         if (_ball->getPosition().x < _screenSize.width * 0.5f - 40 * 0.5f ||             _ball->getPosition().x > _screenSize.width * 0.5f + 40 * 0.5f) {             ballNextPosition.y = _screenSize.height - _ball->radius();             ballVector.y *= -0.8f;             SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");         }     }     //ball and bottom of the court     if (ballNextPosition.y < _ball->radius() ) {         if (_ball->getPosition().x < _screenSize.width * 0.5f - 40 * 0.5f ||             _ball->getPosition().x > _screenSize.width * 0.5f + 40 * 0.5f) {             ballNextPosition.y = _ball->radius();             ballVector.y *= -0.8f;             SimpleAudioEngine::sharedEngine()->playEffect("hit.wav");         }     }      //finally, after all checks, update ball's vector and next position     _ball->setVector(ballVector);     _ball->setNextPosition(ballNextPosition);       //check for goals!     if (ballNextPosition.x  < 50) {         this->playerScore(2);      }     if (ballNextPosition.x > _screenSize.width-50) {         this->playerScore(1);     }      //move pieces to next position     _player1->setPosition(_player1->getNextPosition());     _player2->setPosition(_player2->getNextPosition());     _ball->setPosition(_ball->getNextPosition()); } 

最后還要實現一個得分處理的函數,主要是更新記分牌,歸位,換發球。

void HelloWorld::playerScore (int player) {      SimpleAudioEngine::sharedEngine()->playEffect("score.wav");      _ball->setVector(CCPointZero);      char score_buffer[10];      //if player 1 scored...     if (player == 1) {          _player1Score++;         sprintf(score_buffer,"%i", _player1Score);         _player1ScoreLabel->setString(score_buffer);         //move ball to player 2 court         _ball->setNextPosition(ccp( _screenSize.width* 0.5 + 2 * _ball->radius(), _screenSize.height * 0.5));      //if player 2 scored...     } else {          _player2Score++;         sprintf(score_buffer,"%i", _player2Score);         _player2ScoreLabel->setString(score_buffer);         //move ball to player 1 court         _ball->setNextPosition(ccp( _screenSize.width* 0.5 - 2 * _ball->radius(), _screenSize.height * 0.5));     }     //move players to original position     _player1->setPosition(ccp(_player1->radius() * 2,_screenSize.height * 0.5));     _player2->setPosition(ccp(_screenSize.width - _player1->radius() * 2, _screenSize.height * 0.5));      //clear current touches     _player1->setTouch(NULL);     _player2->setTouch(NULL); } 

最終效果

Android游戲開發十日通(7)- 開發一個雙人游戲



參考

【玩轉cocos2d-x之三】cocos2d-x游戲是怎么跑起來的 -  http://blog.csdn.net/jackystudio/article/details/12554167

【玩轉cocos2d-x之四】cocos2d-x怎么實現跨平臺 - http://blog.csdn.net/jackystudio/article/details/12610287

Cocos2d-x 2.0 自適應多種分辨率 - http://dualface.github.io/blog/2012/08/17/cocos2d-x-2-dot-0-multi-resolution/

Cocos2d-x by Example Beginner's Guide


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

德昌县| 孝义市| 莱芜市| 荃湾区| 库车县| 大同县| 尉氏县| 鹤岗市| 陈巴尔虎旗| 临邑县| 旺苍县| 邵武市| 桦川县| 中江县| 庆元县| 师宗县| 通化市| 英山县| 盐池县| 乌兰察布市| 永川市| 奈曼旗| 安溪县| 小金县| 潼关县| 岚皋县| 阳曲县| 玉田县| 高碑店市| 通化县| 大丰市| 新泰市| 鹿邑县| 中牟县| 临安市| 蒙城县| 海兴县| 治多县| 汝城县| 鹤壁市| 阿荣旗|