Sprite.
ต่อเนื่อจากโพสแรกที่ผ่านมา
สร้างเกมส์ง่ายๆ ด้วย C++ กับ Cocos2d-x ใน Visual Studio และ Eclipse
เรารู้วิธีสร้างโปรเจกต้นแบบไปแล้ว โพสนี้เราจะมาดู
- การใส่รูป (Sprite)
- Listener
ตัวอย่างเกมส์จริงที่ใช้ Cocos2d-x สร้าง
ครั้งนี้เราจะใช้ Eclipse เป็นตัวทำโปรเจกนะครับ ส่วนใครสะดวกจะใช้ VS ก็ไม่เป็นปัญหา
1.
สร้างคลาสขึ้นมาชื่อว่า GmaeScene.h และ GameScene.cpp
GmaeScene.h
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
#ifndef GAMESCENE_H_
#define GAMESCENE_H_
class GameScene {
public:
GameScene();
virtual ~GameScene();
};
#endif /* GAMESCENE_H_ */
{/syntaxhighlighter}
GameScene.cpp
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
#include "GameScene.h"
GameScene::GameScene() {
// TODO Auto-generated constructor stub
}
GameScene::~GameScene() {
// TODO Auto-generated destructor stub
}
{/syntaxhighlighter}
ตอนนี้เราได้เตรียมคลาสไว้แล้ว ต่อไป เราจะไปหาภาพพื้นหลัง กับตัวละคร โพสนี้เราจะใช้แค่สองรูป
รูปที่ 1
พอได้รูปทั้งสองแล้ว ก็ให้เอาไปไว้ที่โฟเดอร์ชื่อ Resources ดังรูป

จากรูป มีสิ่งที่น่าสนใจอยู่สองจุดคือ
- โฟเดอร์ Classes
- โฟเดอร์ Resources
ในโฟเดอร์ classes นี่เราจะเก็บซอสโค๊ดของเราไว้ที่นี่ทั้งหมด อันที่เป็น C++ นะ ส่วนโฟเดอร์ Resources นี่ก็จะเก็บจำพวกรูปภาพ ไฟล์เสียง ไอคอน ฟอร์ต ต่างๆนา ไว้ที่นี้. ที่โปรเจกเขาวางโครงสร้างแบบนี้ก็เพื่อให้รองรับการคอมไพล์ได้หลายๆแพลตฟอร์ม เช่น Anroid,iOS และ Win32 เป็นต้น.
เมื่อเราได้รูปแล้ว ในที่นี้เราะเปลี่ยนชื่อไฟล์จาก 13526287812082.png ไปเป็น GameBackground.png
และ bug-308386_1280.png ไปเป็น Bug.png
ถึงเวลาโค๊คจริงๆละ
2.
อับเดท GameScene.h ให้เป็นตามแบบข้างล่าง
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
#ifndef GAMESCENE_H_
#define GAMESCENE_H_
#include "cocos2d.h"
class GameScene: public cocos2d::Layer {
public:
GameScene();
virtual ~GameScene();
static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC (GameScene);
};
#endif /* GAMESCENE_H_ */
{/syntaxhighlighter}
คุณสามารถเข้าไปดู CREATE_FUNC ได้ ถ้าคุณใช้ Eclipse สามารถก็ Ctrl ค้างไว้ แล้วคลิกไปที่ฟังก์มาโครตัวนั้น
3.
อับเดท GameScene.cpp ตามฟังก์ชั่นที่เราได้เพิ่มไว้ใน GameScene.h ดังต่อไปนี้
ก่อนอื่นเราต้องใช้คำสั่ง using namespace cocos2d; กันก่อน เพราะว่าเวลาเรียนกใช้คลาสต่างๆ จะได้ไม่ต้องระบุ cocos2d namespace ทุกครั้ง
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
#include "GameScene.h"
USING_NS_CC;
{/syntaxhighlighter}
จากนั้น ก็ทำการสร้างฟังก์ชั่นที่เราปรากาศไว้ในไฟล์ header.
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
Scene* GameScene::createScene()
{
auto scene = Scene::create();
auto layer = GameScene::create();
scene->addChild(layer);
return scene;
}
bool GameScene::init()
{
if(!Layer::init())
{
return false;
}
return true;
}
{/syntaxhighlighter}
4.
ตอนนี้เราได้ Scene ใหม่แล้ว พร้อมที่จะใช้งานได้ แทนที่ HelloWorldScene.cpp เรียบร้อยแล้ว ว่าแต่ Scene นีที่นี้หมายถืงอะไร ไปดู
สำหรับ Cocos2d-x แล้ว Scene ก็เหมือนกับฉาก ฉากหนึ่ง. ในหนึ่งฉากนี้ เราสามารถใส่อะไรไปก็ได้ พอตากล้องมาถ่ายทำ เราก็จะเป็นตามเฉพาะที่ซีนนั้นๆได้จัดองค์ประกอบไว้ ซึ้งตอนนี้ GameScene.cpp ของเราเป็นซีนที่ว่างเปล่า ยังไม่มีอะไรทั้งนั้น นั้นก็แสดงว่าถ้า ผู้กำกับมาหันกล้องมาจับตรงนี้ มันก็จะมืดๆ ว่างเปล่า ไม่มีอะไรเลย
ณ ตอนนี้ ผู้กำกับ ( Director::getInstance(); ) กำลังถ่ายทำที่หน้า HelloWorld.cpp อยู่ แต่เราต้องการให้มาถ่ายที่ซีน GameScene.cpp ที่เราเตรียมไว้
5.
อับเดท AppDeligate.cpp ตามนี้
- เพิ่ม
#include "GameScene.h"ต่อจาก#include "HelloWorldScene.h"ประมาณบรรทัดที่ 2 - ในฟังก์ชั่น
bool AppDelegate::applicationDidFinishLaunching()ตรงบรรทัดก่อนreturn true;ให้เปลี่ยนตามนี้
{syntaxhighlighter brush:cpp;collapse:false;first-line:75}
// auto scene = HelloWorld::createScene();
auto scene = GameScene::createScene();
{/syntaxhighlighter}
6.
ถึงตอนนี้ ถ้าสมมุติว่าเราสร้างอะไรเสร็จหมดแล้ว เขียนโค๊ดอะไรเสร็จหมดแล้ว แต่ถ้าเวลาคอมไพล์จริงๆ Cocos2d-x จะไม่เห็นคลาสที่เราสร้างขึ้นมาใหม่ วิธีที่จะบอกให้ Cocos2d-x รู้ว่าต้องคอมไพล์ ไฟล์ไหนบ้าง ให้เข้าไปเพิ่มไฟล์ที่ต้องการจะให้คอมไพล์ได้ที่
/SpriteDemo/jni/Android.mk
ซึ่งเราก็จะเพิ่มได้ดังนี้
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
LOCAL_SRC_FILES := hellocpp/main.cpp
../../Classes/AppDelegate.cpp
../../Classes/HelloWorldScene.cpp
../../Classes/GameScene.cpp
{/syntaxhighlighter}
7.
ลองรันเกมส์นี้ดู ในที่นี้เราจะใช้ Android Emulator สำหรับการทดสอบ
เปิด command prompt ขึ้นมาไปที่โฟเดอร์โปรเจกที่เราสร้าง รันโดยใช้คอมมาน
cocos run –p android
พอรันเสร็จ เราก็จะเห็น scene เปล่าๆ ตามภาพ

8.
ถัดมา เราก็จะมาใส่ Backgroud กันก่อน เปิดไปที่ไฟล์ GameScene.cpp ตามนี้
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
bool GameScene::init()
{
if(!Layer::init())
{
return false;
}
auto background = Sprite::create("GameBackground.png");
this->addChild(background);
return true;
}
{/syntaxhighlighter}
จากนั้นก็รันคอมมานด์เดิม cocos run –p android
9.
คุณก็จะเห็นว่า รูปขึ้น แต่มันไม่เต็มจอ และก็ไม่ตรงกลาง ดังนั้น เราต้งเซ็ตให้มันเต็มจอ และจัดตำแหน่งให้อยู่กึ่งกลาง
{syntaxhighlighter brush:cpp;collapse:false;first-line:1}
bool GameScene::init()
{
if(!Layer::init())
{
return false;
}
Sprite * background = Sprite::create("GameBackground.png");
this->addChild(background);
auto screenSize = Director::getInstance()->getVisibleSize();
background->setScaleX(screenSize.width / background->boundingBox().size.width );
background->setScaleY(screenSize.height / background->boundingBox().size.height );
background->setPosition(screenSize.width * 0.5f, screenSize.height * 0.5f);
return true;
}
{/syntaxhighlighter}
10.
แม้ว่าเราจะพยายาม ขยานขนาดรูปภาพ และจัดให้อยู่กึ่งกลางแล้ว แต่ว่าบางทีมันก็ยังไม่เต็มกรอบอยู่ดี ปัญหานี้แก้ได้โดยการตั้งกฏให้เกมส์ของเราขยายให้เต็มหน้าจอ ทำได้โดยเข้าไปแก้ไข ResolutionPolicy ในคลาส AppDelegate.cpp ในฟังก็ชั่น bool AppDelegate::applicationDidFinishLaunching()
ทำการเปลี่ยนจาก
{syntaxhighlighter brush:cpp;collapse:false;first-line:54}
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
{/syntaxhighlighter}
ไปเป็น
{syntaxhighlighter brush:cpp;collapse:false;first-line:54}
glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy:: EXACT_FIT);
{/syntaxhighlighter}
เราก็จะได้แบกกราวน์แสดงตามที่ต้องการแล้ว
11.
ต่อไป จากรูปแมลงที่เราเซฟมา เราจะกำหนดให้ว่า ถ้าคลิกตรงไหน ก็ให้แสดงรูป บั๊กนั้นตรงนั้น โค๊ดดังกล่าว สามารถทำได้เช่น
{syntaxhighlighter brush:cpp;collapse:false;first-line:29}
bool GameScene::init()
{
if(!Layer::init())
{
return false;
}
auto * background = Sprite::create("GameBackground.png");
this->addChild(background);
auto screenSize = Director::getInstance()->getVisibleSize();
background->setScaleX(screenSize.width / background->boundingBox().size.width );
background->setScaleY(screenSize.height / background->boundingBox().size.height );
background->setPosition(screenSize.width * 0.5f, screenSize.height * 0.5f);
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = [&](Touch * touch, Event * event ){
Sprite * bug = Sprite::create("Bug.png");
bug->setPosition(touch->getLocation());
this->addChild(bug);
return true;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener,this);
return true;
}
{/syntaxhighlighter}
บรรทัดที่ 46 auto listener = EventListenerTouchOneByOne::create(); คือการสร้าง listener มาอันหนึ่ง จากนั้นเราสร้างแลมดาขึ้นมาอีกอันหนึ่งไป assigned ให้กับ onTouchBegan
** หากคุณต้องการดูการใช้แลมดาอย่างคร่าวๆใน C++ สามาระดูได้ที่นี้ (ภาษาไทย )
ซึ้งแลมดานี้ไม่มีอะไรมาก ก็แค่สร้าง sprite ขึ้นมาอันหนึ่ง แล้วก็เซ็ตตำแหน่ง (แต่ยังไม่ได้เซตขนาด) ตามตำแหน่งที่เรากดๆ จิ้มๆ ลงไป จากนั้นก็วาดรูปแมลงใส่ในเลเยอร์นั้นๆ
บรรทัดที่ 54 เป็นการรีจิเตอร์ให้รู้ว่า this เนี้ยะจะทำการจับ event ที่ชื่อว่า listener ที่เราประกาศไว้
ก็จะได้ประมาณนี้

12.
ยินดีด้วยครับ คุณทำเสร็จแล้ว
จากเกมส์แรกที่เราทำไป ปัญหาหนึ่งคือรูปแมลงจะใหญ่เกินไป คุณลองทำให้มันเล็กลง เช่น เซตขนาดของมันก่อน ก่อนที่จะเอาไปใส่
ติดปัญหาหรือสงสัยตรงไหนสอบถามได้
อ่านแล้วคิดถึง XNA มากครับ
johnny.sayasane Fri, 16/10/2015 - 22:44
อ่านแล้วคิดถึง XNA มากครับ อยากกลับไปเหมือนตอนยังเรียนอยู่จัง อยากทำอะไรก็ทำ ตื่นมาโค็ด เย็นมาวาดรูป วนลูปอย่างงี้มีความสุขจริงๆ T^T
ไม่ได้ทำตามต้องขอโทษด้วยครับ แต่ชอบอ่านแล้วมีความสุขไงไม่รู้ ตอนนั้นจับ XNA ครั้งแรก แล้วทำให้ sprite เคลื่อนที่ได้ ตอนนั้นนี่ผมโดดจากเก้าอี้เลยจริงๆ 555 เหมือนบ้า
ครับผม ตอนนี้ก็ทำงานเหมือนกัน
KoneCth Sat, 17/10/2015 - 06:50
In reply to อ่านแล้วคิดถึง XNA มากครับ by johnny.sayasane
ครับผม ตอนนี้ก็ทำงานเหมือนกัน จะมีก็แค่ช่วงเวลาว่างๆที่มีเพียงน้อยนิดในแต่ละสัปดาห์เอามาทำในสิ่งที่ทำแล้วมีความสุขครับ XNA ดูน่าสนใจดี แต่เหมือนตอนนี้เขาไม่พัฒนาต่อแล้วหรืออย่างไรครับ ?
เขาไม่ได้ไปต่อแล้วละครับ
johnny.sayasane Sun, 18/10/2015 - 00:58
In reply to ครับผม ตอนนี้ก็ทำงานเหมือนกัน by KoneCth
เขาไม่ได้ไปต่อแล้วละครับ แต่มีคนเอามาเขียนใหม่หลายอันเช่น MonoGame, ANX, SharpDX... ที่นิยมกันก็เห็นจะเป็น MonoGame นะ แต่ทาง MS รู้สึกจะพลักให้ไปใช้ Unity เสียมากกว่าครับ
อ่องี้นี้เอง ขอบคุณครับ
KoneCth Mon, 19/10/2015 - 19:49
In reply to เขาไม่ได้ไปต่อแล้วละครับ by johnny.sayasane
อ่องี้นี้เอง ขอบคุณครับ
ส่วนตัวผมลอง Cocos
mr_tawan Tue, 27/10/2015 - 00:53
ส่วนตัวผมลอง Cocos แล้วไม่ค่อยชอบ รู้สึกว่ามันเยอะแยะตาแปะไก่เกินไป ผมก็เลยกลับไปใช้ SDL 2.0 เหมือนเดิม ได้ลงไปลุยโค๊ดระดับล่างสนุกกว่าน่ะครับ (แต่เข้าใจว่าถ้าทำแบบนั้นเยอะ ๆ แล้วเกมจะไม่เสร็จซะที)
ใช่เลยครับมันสนุกกว่าจริงๆ
johnny.sayasane Tue, 27/10/2015 - 01:40
In reply to ส่วนตัวผมลอง Cocos by mr_tawan
ใช่เลยครับมันสนุกกว่าจริงๆ เหมือนเสพติด 55
เอาจริง ๆ คือ
mr_tawan Tue, 27/10/2015 - 03:49
In reply to ใช่เลยครับมันสนุกกว่าจริงๆ by johnny.sayasane
เอาจริง ๆ คือ ที่ตอนนี้เขียนอยู่คือ Visual Novel ซึ่งจะใช้ฟีเจอร์ด้านตัวหนังสือประมาณนึง ปัญหาที่ผมเจอสำหรับ Cocos ก็คือการตัดบรรทัดใน Rich Text ซึ่งมันเพี้ยนไปเลย เพราะใน Rich Text มันจะประกอบด้วย Label ย่อย ๆ ถ้าเราใส่ \n ปุ๊บมันก็จะตัดใน Label ย่อย ๆ แทน (ผมแจ้งไปแล้วเรื่องนี้ น่าจะมีแก้ละ) และผมก็มีบอกอีกว่าสำหรับภาษาไทยเราใช้ manual break เยอะก็เลยจำเป็นต้องมี
แล้วก็ถ้าใช้ภาษาไทยก็จะเจอเรื่องวรรณยุกต์จม-วรรณยุกต์ลอย ซึ่งก็เพราะว่าตัว Text Layout Engine มันไม่ได้รองรับ Unicode Feature มากมายอะไร Kerning ก็ทำงานแปลก ๆ ผมมีเสนอให้เอา harfbuzz ที่เป็น opentype engine เสริมไปกับตัว freetype ที่เป็น font renderer ซึ่งจะมีข้อดีอีกอย่างคือลูกเล่นมันแพรวพราวมาก วาดตัวหนังสือแนวตั้งยังทำได้เลย (อันนี้เห็นคนจีน-ญี่ปุ่นสนใจอยู่)
พอเจออะไรเยอะ ๆ ผมก็เลยคิดว่าจะ implement ตัว rich text ของตัวเอง ปัญหาอีกอย่างที่เจอคือการเอา library อื่น ๆ เข้าไปใส่ในตัว Cocos นั้นค่อนข้างน่าปวดหัวเลย ... แล้วตัวมันเองก็ค่อนข้างใหญ่ ใช้ 3rd party เยอะ ผมก็เลยตัดใจย้อนกลับมา SDL2 ซึ่งมันอาจจะดูบ้านๆ ไปหน่อย แต่ผมก็ควบคุมอะไรเองได้แทบทุกอย่างครับ
ตอนนี้เพิ่ง implement ตัว label เสร็จ เดี๋ยวจะโดดไปเขียน Rich Text ซึ่งคงใช้เวลาไม่เยอะ น่าจะเป็นแนว ๆ เดียวกับที่ Cocos ใช้ครับ เขาออกแบบตัว api ได้โอเคเลยนะ
ผมเคยทำแนวๆ Visual Novel กับ
johnny.sayasane Tue, 27/10/2015 - 23:32
In reply to เอาจริง ๆ คือ by mr_tawan
ผมเคยทำแนวๆ Visual Novel กับ XNA อยู่แต่ไม่ได้ใส่รายละเอียดเยอะขอแค่อ่านได้ (ภาษาลาว) กว่าจะทำให้มันอ่านได้นั้นก็ทำนานมากจำไม่ได้แล้วว่าทำอะไรลงไปบ้าง ยังดีที่ภาษาไทย กับ ลาว ใช้ระบบใกล้เคียงกันเลยพอหาทางออกได้อยู่ นี่ก็ไม่ได้จับโค็ดนาน 2 ปีแล้ว เพราะทำงานเป็นอาร์ทไดกราฟฟิกอย่างเดียวไม่มีเวลาเลย ปีหน้าบอกกับตัวเองไว้แล้วว่าจะกลับเข้าสู่วงการเดิมครับ XD