write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
讨论新闻组及文件
其实在上一节,游戏已经基本成型了,但是还不能算是完整的游戏,本篇将需要完善的部分完成吧。
这个实在不需要我额外讲方法了,按原来的办法加上四周的边框即可。不过这里讲几个个人总结的技巧,边框需不需要显示呢?需要显示的话,那没有话说,直接创造一个边框就可以了,不需要显示呢?我想出了两个办法,其一,将边框创造在显示范围以外,那么自然是看不见了。其二,创建完全透明的图,那么你就可以将边框放置在任何你想要加碰撞阻挡,却不希望玩家看到的位置了。其三,不透明的图其实也行,只需要放置在Z轴超出视景体即可(大于远剪裁面,小于等于近剪裁面),由于box2D是2D的物理引擎,会忽略Z轴,同样的,只要X,Y轴对了,你还是能够获取到你想要的碰撞效果。
这里由于懒得做资源了,直接使用Orx作者的资源和配置,所以使用第一种方法。
首先,用作者的wall.png创造四周的墙壁,为了先看到直观的看到碰撞效果,同时也为了大概确认位置正确,我们首先将墙壁置为可见。
配置如下:
; Wall
[WallTemplate]
Graphic = WallGraphic
Body = WallBody
BlendMode = alpha
[WallGraphic]
Texture = data/wall.png
Pivot = center
[Walls]
ChildList = Wall1 # Wall2 # Wall3 # Wall4
[Wall1@WallTemplate]
Position = (0.0, 250, 0.0)
Rotation = 90.0
[Wall2@WallTemplate]
Position = (-170, 0.0, 0.0)
[Wall3@WallTemplate]
Position = (0.0, -250, 0.0)
Rotation = 90.0
[Wall4@WallTemplate]
Position = (170, 0.0, 0.0)
[WallBody]
PartList = WallBox
Dynamic = false
[WallBox]
Type = box
Friction = 1.0
Restitution = 0.0
SelfFlags = 0x0001
CheckMask = 0x0001
Solid = true
我新建了一个名为wall.ini的配置文件,此时在原来的game.ini中添加下面这条语句表示包含。
@wall.ini@
其他的配置的含义在上一节中已经提到过,这里不重复了。
然后,在初始化的时候新添加一条代码创造墙壁。
if (orxObject_CreateFromConfig("Walls") == NULL) {
result = orxSTATUS_FAILURE;
}
看到这里,提一下Orx作者iarwain推荐的方法,iarwain推荐大量的不需要直接获取指针的对象可以通过一个类似于scene的配置端来配置,然后通过ChildList字段串起来,由于一个ChildList的列表长度是255个,(已经够多了)假如还不够的话,任意一个ChildList指定的对象还可以是仅包含ChildList的字段。。。。。如此串起来,无穷尽也。。。。以此构建整个场景。。。。。好处是可以动态添加新的东西,完全不用像我一样添加代码。
然后,就可以看到正常的碰撞效果了。而且也可以看到墙壁存在。要将难看的墙壁消去,只需要修改配置将墙壁移到显示范围外即可(记得计算墙壁本身的宽度)
此例中部分配置实际的为如下内容即可:(将墙都往屏幕外移动10像素)
[Wall1@WallTemplate]
Position = (0.0, 260, 0.0)
Rotation = 90.0
[Wall2@WallTemplate]
Position = (-180, 0.0, 0.0)
[Wall3@WallTemplate]
Position = (0.0, -260, 0.0)
Rotation = 90.0
[Wall4@WallTemplate]
Position = (180, 0.0, 0.0)
有人将游戏称为第9艺术,并且,游戏与电影的区别就在于更多的交互,这里,我们来谈谈交互^^当然,那就是响应玩家的输入,并且做出反应罗。这里是用Win32平台来做例子的,所以,就说键盘输入吧。
首先,为了不在Run函数中去响应键盘输入,我添加一个新的自己的Update函数,需要是用一个计时器,相关的知识可以参考官方的中文WIKI 。
需要用下面两句注册一个Update的回调函数,这里创建的是20HZ的clock。
orxCLOCK *pstClock = orxClock_Create(orx2F(0.05f ), orxCLOCK_TYPE_USER);
orxClock_Register(pstClock, GameApp::Update, NULL , orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
然后就可以在Update里面处理我们的的输入了。
首先,配置部分,很简单。
[Input]
SetList = Input
KEY_LEFT = GoLeft
KEY_RIGHT = GoRight
主要就是配置Input配置段,这里的SetList一般情况下可以是其他配置段的名字,并且可以是一个list,这里为求简单,我指向了自身(配置简单化大法之一),这样就可以省下一个section。这里的配置表示按左方向键表示GoLeft的动作,按右方向键表示GoRight的动作。
我们在代码里面看GoLeft和GoRight怎么用的:
void GameApp::Update(const orxCLOCK_INFO *_clock_info, void *_context)
{
#define MOVE_SPEED 10
if ( orxInput_IsActive("GoLeft" ) ) {
orxVECTOR pos;
orxObject_GetPosition(gPaddle, &pos);
pos.fX -= MOVE_SPEED;
orxObject_SetPosition(gPaddle, &pos);
} if (orxInput_IsActive("GoRight" )) {
orxVECTOR pos;
orxObject_GetPosition(gPaddle, &pos);
pos.fX += MOVE_SPEED;
orxObject_SetPosition(gPaddle, &pos);
}
}
这里的gPaddle就是全局的paddle指针。然后,此时的游戏已经可以玩了。按左右方向键移动paddle即可。
这里再贴一下新的完整代码,有部分修改,还有一些改进前一节教程的内容
1
2 #include "orx.h"
3
4 #include
5 #include
6 using namespace std;
7 orxOBJECT* gPaddle;
8 class GameApp
9 {
10 public :
11 static orxSTATUS orxFASTCALL EventHandler(const orxEVENT *_pstEvent);
12 static orxSTATUS orxFASTCALL Init();
13 static void orxFASTCALL Exit();
14 static orxSTATUS orxFASTCALL Run();
15 static void orxFASTCALL Update(const orxCLOCK_INFO *_clock_info, void *_context);
16
17 GameApp() {};
18 ~GameApp() {};
19
20 static GameApp* Instance() {
21 static GameApp instance;
22 return &instance;
23 }
24
25 private :
26 orxSTATUS InitGame();
27 };
28
29 #define BLOCK_TYPE 1
30
31 orxOBJECT *CreateText(orxSTRING _zTextSection)
32 {
33 orxConfig_PushSection(_zTextSection);
34 orxConfig_SetString("Graphic" , _zTextSection);
35 orxConfig_SetString("Text" , _zTextSection);
36
37 orxOBJECT *pstReturn = orxObject_CreateFromConfig(_zTextSection);
38
39 orxConfig_PopSection();
40
41 return pstReturn;
42 }
43
44 // Init game function
45 orxSTATUS GameApp::InitGame()
46 {
47 orxSTATUS result = orxSTATUS_SUCCESS;
48 orxCLOCK *pstClock = orxClock_Create(orx2F(0.05f ), orxCLOCK_TYPE_USER);
49 orxClock_Register(pstClock, GameApp::Update, NULL , orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
50
51
52 // Creates viewport
53 if ( orxViewport_CreateFromConfig("Viewport" ) == NULL ) {
54 result = orxSTATUS_FAILURE;
55 }
56
57 if (orxObject_CreateFromConfig("Ball" ) == NULL ) {
58 result = orxSTATUS_FAILURE;
59 }
60
61
62 if ( (gPaddle = orxObject_CreateFromConfig("Paddle" )) == NULL ) {
63 result = orxSTATUS_FAILURE;
64 }
65
66 if (orxObject_CreateFromConfig("Blocks" ) == NULL ) {
67 result = orxSTATUS_FAILURE;
68 }
69
70 if (orxObject_CreateFromConfig("Walls" ) == NULL ) {
71 result = orxSTATUS_FAILURE;
72 }
73
74 orxEvent_AddHandler(orxEVENT_TYPE_PHYSICS, GameApp::EventHandler);
75 // Done!
76 return result;
77 }
78
79 // Event handler
80 orxSTATUS orxFASTCALL GameApp::EventHandler(const orxEVENT *_pstEvent)
81 {
82 orxSTATUS eResult = orxSTATUS_SUCCESS;
83 if (_pstEvent->eType == orxEVENT_TYPE_PHYSICS) {
84 if ( _pstEvent->eID == orxPHYSICS_EVENT_CONTACT_REMOVE ) {
85 /* Gets colliding objects */
86 orxOBJECT *object_recipient = orxOBJECT(_pstEvent->hRecipient);
87 orxOBJECT *object_sender = orxOBJECT(_pstEvent->hSender);
88
89 string recipient_name(orxObject_GetName(object_recipient));
90 string sender_name(orxObject_GetName(object_sender));
91 if (sender_name.substr(0 , sender_name.length()-1 ) == "Block" ) {
92 // 这样比直接删除要安全
93 orxObject_SetLifeTime(object_sender, orxFLOAT_0);
94 }
95 }
96 }
97 // Done!
98 return orxSTATUS_SUCCESS;
99 }
100
101 void GameApp::Update(const orxCLOCK_INFO *_clock_info, void *_context)
102 {
103 #define MOVE_SPEED 10
104 if ( orxInput_IsActive("GoLeft" ) ) {
105 orxVECTOR pos;
106 orxObject_GetPosition(gPaddle, &pos);
107 pos.fX -= MOVE_SPEED;
108 orxObject_SetPosition(gPaddle, &pos);
109
110 } if (orxInput_IsActive("GoRight" )) {
111 orxVECTOR pos;
112 orxObject_GetPosition(gPaddle, &pos);
113 pos.fX += MOVE_SPEED;
114 orxObject_SetPosition(gPaddle, &pos);
115 }
116 }
117 // Init function
118 orxSTATUS GameApp::Init()
119 {
120 orxSTATUS eResult;
121 orxINPUT_TYPE eType;
122 orxENUM eID;
123
124 /* Gets input binding names */
125 orxInput_GetBinding("Quit" , 0 , &eType, &eID);
126 const orxSTRING zInputQuit = orxInput_GetBindingName(eType, eID);
127
128 // Logs
129 orxLOG(" n - ' %s ' will exit from this tutorial"
130 " n * The legend under the logo is always displayed in the current language" , zInputQuit );
131
132 orxLOG("Init() called!" );
133
134 // Inits our stand alone game
135 eResult = GameApp::Instance()->InitGame();
136
137 // Done!
138 return eResult;
139 }
140
141 // Exit function
142 void GameApp::Exit()
143 {
144
145 // Logs
146 orxLOG("Exit() called!" );
147 }
148
149 // Run function
150 orxSTATUS GameApp::Run()
151 {
152 orxSTATUS eResult = orxSTATUS_SUCCESS;
153
154
155 // Done!
156 return eResult;
157 }
158
159
160 // Main program function
161 int main(int argc, char **argv)
162 {
163 // Inits and runs orx using our self-defined functions
164 orx_Execute(argc, argv, GameApp::Init, GameApp::Run, GameApp::Exit);
165
166 // Done!
167 return EXIT_SUCCESS ;
168 }
169
170
171 #ifdef __orxMSVC__
172
173 // Here's an example for a console-less program under windows with visual studio
174 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
175 {
176 // Inits and executes orx
177 orx_WinExecute(GameApp::Init, GameApp::Run, GameApp::Exit);
178
179 // Done!
180 return EXIT_SUCCESS ;
181 }
182
183 #endif // __orxMSVC__
一共183行。我并没有特意的精简代码,比如有两个可供切换的Main函数部分,有很多可以Init部分其实也都可以通过iarwain的方式缩减。
全部的源代码在:https://orx-sample.jtianling.googlecode.com/hg/
上,可以通过mercurial直接获取。
这里同样提供一个对比的参考。
《How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 1/2 》
《How To Create A Breakout Game with Box2D and Cocos2D Tutorial: Part 2/2 》
点击链接下载源代码:Cocos2D and Box2D Breakout Game
原创文章作者保留版权 转载请注明原作者 并给出链接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie