Thứ Tư, 23 tháng 10, 2013

7 tính năng đặc sắc trên iOS 7 được xem là vay mượn từ Android

iOS 7 là sự đổi mới mạnh mẽ nhất của Apple kể từ khi ra mắt HĐH iOS, tuy nhiên nó cũng vấp phải nhiều soi mói và chỉ trích bởi giao diện màu mè, dễ rối mắt... đặc biệt, tính năng mới trên iOS 7 còn bị "lên án" khi được xem là vay mượn khá nhiều từ các HĐH khác và trong đó có lẽ nhiều nhất là Android.

Control Center

Không thể phủ nhận rằng Apple đã tích cực đổi mới HĐH iOS của mình với việc lần đầu tiên tạo ra được một trung tâm Control Center. Tính năng này giúp kiểm soát và xử lý hầu hết các tác vụ quan trọng trên máy như bật/tắt Wi-Fi, Bluetooth, điều chỉnh độ sáng, trình phát nhạc và các phím tắt cho đèn flash, đồng hồ,máy tính và camera, đặc biệt tất cả sẽ hiển thị ngay tức khắc khi bạn vuốt từ phía dưới màn hình lên.

Tuy nhiên, trên HĐH Android, tính năng này dường như đã xuất hiện từ khá lâu ngay từ các phiên bản đầu tiên cũng với tên gọi là "control center" nhưng sẽ phải thao tác ngược lại khi vuốt từ trên xuống. Trong đó, các điều chỉnh bạn có thể làm với tính năng này là bật/tắt Wi-Fi, Bluetooth, điều chỉnh độ sáng...

Đặc biệt với tính năng trên, HĐH Android còn cho thấy sự vượt trội hơn iOS 7 trong khả năng tùy biến với ứng dụng cài đặt nhanh (1Tap QuickBar), cho phép người dùng có thể tùy ý thêm vào các ứng dụng hoặc phím tắt khác theo ý mình, trong khi đó với HĐH iOS 7, điều đó dường như vẫn quá xa vời.

Air Drop


Là tính năng có mặt trên iPhone 5 và đến các thế hệ iPhone mới ra vừa qua, nó cho phép người dùng có thể chia sẻ nội dung đa phương tiện như hình ảnh hoặc video khi các thiết bị này được đặt ở gần nhau.

Trong khi đó, với Android từ phiên bản 4.0 trở đi, Google đã đưa vào tính năng Android Beam để tối ưu hóa trong việc sử dụng các kết nối có tác dụng tương tự khác như NFC hay Bluetooth. Cụ thể với công nghệ NFC tích hợp sẵn bên trong, người dùng các thiết bị Android có thể chuyển đổi các dữ liệu liên quan đến bản đồ, hình ảnh, liên kết web, địa chỉ liên lạc...

Còn nếu bạn không thích sử dụng tính năng trên mà muốn sử dụng thông qua một ứng dụng thì Bump là một ứng dụng rất phù hợp để làm được điều đó.

Tính năng camera


iOS 7 có tới hơn 200 tính năng mới và các tính năng liên quan đến máy ảnh cũng là điểm có thể thu hút phần lớn người dùng. Trong tính năng chụp hình này, Apple đã đưa vào các cải tiến, làm mới lại giao diện và các chế độ căn chỉnh cũng như lọc hình ảnh sau chụp mới.

Còn với Android, tính năng camera dường như lại có sự biến đổi tùy theo các nhà sản xuất khác nhau sử dụng chung nền tảng Android và vì thế rất khó để có thể tìm ra điểm giống và khác giữa hai tính năng camera của hai HĐH.

Cũng có các ứng dụng có thể đảm nhiệm chức năng chụp ảnh nhanh và quay chậm, hệ điều hành Android sở hữu 2 ứng dụng tương tự có tên Fast Burst Camera Lite hoặc AndroVid Video Trimmer với nhiều lượt tải về và đánh giá tốt về ứng dụng.

Xử lý đa nhiệm


Trước khi chưa có phiên bản iOS 7, người dùng có thể chuyển đổi giữa các ứng dụng bằng thao tác vuốt ngang hoặc cũng có thể nhấn đúp nút Home để thực hiện nhưng đặc biệt với iOS 7 mới này, người dùng sẽ được nhìn các bản xem trước của các ứng dụng đang chạy ẩn và xóa đi bằng một cái vuốt lên, một điều ấn tượng mà các phiên bản trước chưa làm được.

Android thì khác, hệ điều hành này vốn đã rất nổi tiếng trước iOS với khả năng tùy biến và đa nhiệm xuất sắc, chỉ bằng việc kéo dài nút Home là bạn đã có thể xóa ứng dụng và với nhiều phiên bản Android, bạn cũng có thể tắt ứng dụng trong thanh Control Center.

Trung tâm thông báo


Tính năng thông báo mới trên iOS 7 là tính năng mới giúp người dùng có thể nhận và xem trực tiếp được các thông báo quan trọng như mail, thời tiết, lịch và các sự kiện đã bị lỡ, tất cả sẽ có thể hiện ra dễ dàng dưới mắt người dùng bằng cách vuốt nhẹ màn hình từ trên xuống dưới, trái với việc mở Control Center.

Trái lại, tùy thuộc vào điện thoại của bạn đang chạy Android tùy biến của hãng nào thì tính năng thông báo trên hệ điều hành Android cũng sẽ có sự thay đổi nhưng nhìn chung vẫn sẽ có các thông tin sự kiện, giao thông, thời tiết, thể thao...và có sự trợ giúp từ ứng dụng hữu ích Google Now được tích hợp sẵn.

Gọi thoại âm thanh trực tiếp qua FaceTime


Khác với chức năng chính của FaceTime là gọi điện trực tiếp bằng hình ảnh giữa hai người thông qua một kết nối Wi-Fi hoặc 4G đủ khỏe. Với iOS 7, người dùng còn có thêm tính năng gọi điện qua FaceTime chỉ bằng giọng nói được chuyển đi giữa hai người.

Nhưng Android cũng không kém cạnh dù không tạo ra được một ứng dụng gọi điện mặc định nào như FaceTime của Apple thì HĐH này cũng có những sự trợ giúp khá đắc lực từ các ứng dụng gọi sử dụng giao thức VoIP như Skype, Tango để có thể tha hồ gọi, nhắn tin và thậm chí là cả chat video miễn phí.

Safari trở nên thông minh hơn


Như đã biết, các tính năng mới trên trình duyệt mặc định của iOS là Safari cũng có nhiều sự thay đổi quan trọng nhằm tăng tính trải nghiệm cho người dùng như hợp nhất thanh gõ địa chỉ URL và thanh tìm kiếm, trình quản lý tab xem trước được, duyệt web riêng tự được tích hợp sẵn và tính năng bảo mật quan trọng Do not Track.

Apple iOS tự hào với trình duyệt Safari bao nhiêu thì Google cũng có niềm tự hào riêng của mình là Chrome bấy nhiêu và đồng thời trình duyệt này cũng là trình duyệt mặc định trên Android với nhiều các tính năng ấn tượng không kém Safari như quản lý tab xem trước, mở tab ẩn danh, tìm kiếm thông minh và Do not Track.

BlackBerry sụp đổ: Do Android hay iPhone?

Khi thương hiệu BlackBerry sụp đổ, nhiều người đã đặt ra câu hỏi, Android hay iPhone là nguyên nhân khiến cho thương hiệu này không còn chỗ đứng trên thị trường?

Khi doanh số bán hàng của BlackBerry bị lao dốc, đã có nhiều ý kiến cho rằng, công ty đã phản ứng quá chậm trước mối đe dọa khổng lồ là dòng sản phẩm iPhone của Apple, do đó, việc kinh doanh thất bại là điều không tránh khỏi.

Tuy nhiên, nghiên cứu mới đây từ Business Insider đã chỉ ra rằng, sau khi iPhone xuất hiện trên thị trường (năm 2007), điện thoại BlackBerry vẫn là 1 thương hiệu được nhiều người lựa chọn. Doanh số bán BlackBerry chỉ thực sự không như mong muốn khi có sự xuất hiện của dòng smartphone Android.

Các chuyên gia tại Business Insider cho biết, mọi việc tồi tệ đến với BlackBerry từ năm 2010, thời điểm thị phần của Android bùng nổ trên thị trường điện thoại di động. Ngay cả khi Android nắm giữ tới 80% thị phần thị trường, thị phần của Apple duy trì ở mức 15-20% trong vòng 4 năm qua.

Vì thế, có thể hình dung rằng, nếu không có smartphone Android tại các thị trường mới nổi, có thể, người dùng vẫn sẽ lựa chọn BackBerry bởi mức giá iPhone nằm ngoài khả năng chi trả của họ.

Tuy nhiên, trên thực tế, Samsung, Xiaomi, ZTE và nhiều hãng công nghệ khác trên thị trường đã phát triển một loạt các thế hệ smartphone giá rẻ với nhiều tính năng và ứng dụng hiện đại. Vì thế, người dùng có nhiều cơ hội lựa chọn hơn và điện thoại BlackBerry bắt đầu đối mặt với những khó khăn, thử thách mới.

Nói cách khác, những người đã từng đổ lỗi cho iPhone khiến cho BlackBerry không còn chỗ đứng trên thị trường nên xem xét lại. Chính Android với các smartphone giá rẻ là nguyên nhân khiến cho “tượng đài” smartphone một thời sụp đổ.

Sau những thua lỗ kéo dài triền miên, đầu tháng 8 vừa qua, BlackBerry đã rao bán công ty. Tuần trước, BlackBerry cho biết, công ty đã chấp nhận bán mình cho hãng đầu tư tài chính có trụ sở tại Toronto (Canada) Fairfax Financial Holdings với giá 4,7 tỉ USD. Được biết, Fairfax là một trong những cổ đông lớn nhất tại BlackBerry. Hãng tài chính này hiện đang nắm giữ 10% cổ phần tại BlackBerry.

iPhone thân thiện với hệ điều hành gấp 3 lần smartphone Android

Theo nghiên cứu mới đây nhất, iPhone là dòng smartphone sở hữu nhiều phiên bản cập nhật hệ điều hành nhất trong khi một số smartphone Android chỉ sở hữu một vài hoặc chỉ một lần cập nhật.

Những smartphone Android có xu hướng bị lãng quên sau vài phiên bản cập nhật hệ điều hành.
Với sự xuất hiện của hàng loạt thiết bị trên thị trường, việc sở hữu cho mình một chiếc smartphone không còn là vấn đề khó khăn với nhiều người. Tuy nhiên, liệu bạn có bao giờ nghĩ rằng sản phẩm của mình sẽ tồn tại được bao nhiêu lâu sau những đợt cập nhật phần mềm?

Các phiên bản hệ điều hành trên di động thường được cải thiện rất lớn về hiệu năng, tính năng cũng như những tiện ích. Thế nhưng không phải nhà phát triển nào cũng dung hòa được các yếu tố này lên những thiết bị "cổ". Có vẻ như nếu bạn là một người dùng Android, bạn sẽ dễ "lạc" mất các phiên bản cập nhật hơn.

Trong thống kê mới đây, một số thế hệ iPhone vẫn có thể cập nhật hệ điều hành sau hơn 4 năm xuất hiện, trong khi đó những chiếc smartphone Android chỉ có khoảng 2 năm trước khi "lỗi thời". Tất nhiên thống kê này không tính đến những phiên bản hệ điều hành được "tái chế" tại ví dụ như các phiên bản Android gắn mác CyanogenMod.

Trong khi đó người dùng iPhone có thể thoải mái cập nhật hệ điều hành hơn với vòng đời sản phẩm dài hơn.

Chiếc điện thoại được quan tâm nhiều nhất chính là sản phẩm iPhone 3GS của Apple khi có thể sử dụng được nhiều phiên bản hệ điều hành. Vòng thời của iPhone 3GS có thể kéo dài tới 4,3 năm qua những phiên bản cập nhật hệ điều hành khác nhau. Thế nhưng khi so sánh với đối thủ Samsung cùng dòng Galaxy S, những sản phẩm này chỉ có thể được cập nhật trong vòng 2 năm từ khi xuất hiện trên thị trường.

Được ra mắt với hệ điều hành iOS 4, iPhone 3GS vẫn được Apple hỗ trợ tới iOS 6.
Đối với dòng Nexus của Google cũng không phải là ngoại lệ, chiếc Galaxy Nexus của Samsung phối hợp cùng Google có lẽ là sản phẩm tồn tại được lâu nhất qua các phiên bản cập nhật Android khi có thời gian sử dụng lên tới 1,9 năm. Những sản phẩm Android khác có xu hướng bị lãng quên nhanh chóng hơn, có một số sản phẩm chỉ sử dụng được trong 1 năm và không thể nâng cấp thêm được nữa.


Trong khi đó chiếc Galaxy Nexus được ra mắt với hệ điều hành Android 4.0 ICS và hiện tại đang có thể sử dụng được Android 4.3 Jelly Bean thế nhưng khả năng nâng cấp lên Android 4.4 KitKat hay cao hơn vẫn còn quá mờ mịt cho Galaxy Nexus.

Tất nhiên, nếu bạn là một người dùng có điều kiện kinh tế ổn định và có thể thường xuyên thay thế smartphone, đây sẽ không phải là vấn đề lớn. Tuy nhiên nếu như bạn muốn có một thiết bị sử dụng được lâu dài, có những bản cập nhật tính năng mới đa dạng thì iPhone là sự lựa chọn hàng đầu.

Thứ Năm, 17 tháng 10, 2013

Hướng dẫn tạo một Game đơn giản với AndEngine 2.0 (Phần 3)

Nào chúng ta lại tiếp tục phần 3 nhé, trong phần này chúng ta sẽ tạo ra đạn để bắn vào vật thể.

OK! Trước khi vào phần này bạn bạn nên tham khảo Object pool của bài viết TKminh ở vietandroid.com nhé, ở đây tôi không giới thiệu nó.

Let’s start:

Để bắn đi từ Ship tới mục tiêu, chúng ta cần những viên đạn, vì vậy chúng ta sẽ tạo ra đạn ở đây: Ta tạo ra lớp đạn và đặt tên là Bullet

Mã:
public class Bullet {

public Rectangle sprite;//Đạn hình vuông nhỏ
public Bullet(){
sprite = new Rectangle(0,0,10,10,AndEngineTutorial2Activity.getSharedInstance().
getVertexBufferObjectManager());
sprite.setColor(1f,0f,0f);
}
}

Mỗi viên đạn là 1 hình vuông có size 10x10 pixels, và màu nó là màu đỏ (RGB). Ở đây bạn cũng có thể sử dụng hình ảnh thay vì các viên đạn bạn chỉ cần thay sprite thành Sprite và điều chỉnh một chút bên trong lớp là OK.

OK Bây giờ chúng ta sẽ sử dụng pool để tái sử dụng lại các viên đạn
Chúng ta tạo ra 1 lớp mới đặt tên là: BulletPool
Mã:
public class BulletPool extends GenericPool<Bullet> {
public static BulletPool instance;

public static BulletPool sharedBulletPool() {
if (instance == null)
instance = new BulletPool();
return instance;
}

private BulletPool() {
super();
}

@Override
protected Bullet onAllocatePoolItem() {
// TODO Auto-generated method stub
return new Bullet();
}

// Dung de Recycle 1 bullet
protected void onHandleRecycleItem(final Bullet b) {
b.sprite.clearEntityModifiers();
b.sprite.clearUpdateHandlers();
b.sprite.setVisible(false);
b.sprite.detachSelf();
}
}



Đầu tiên chúng ta cần phải theo dõi xem viên đạn bắn ra từ đâu, và đi như thế nào từ đâu đến đâu để chúng ta còn loại bỏ nó. Và khi viên đạn gặp cái gì đó (đích, hoặc ngoài màn hình).

Bây giờ chúng ta hãy trở lại lớp GameScene tạo mới LinkedList của các viên đạn và biến để đếm chúng.
Mã:
 
public int bulletCount;
public LinkedList<Bullet> bulletList;
Bên trong Constructor chúng ta new bulletlist
Mã:
       bulletList = new LinkedList<Bullet>();
Chúng ta trở lại lớp Ship và them vào method (shoot() ) như sau:
Mã:
 
public void shoot() {
if (!moveable)
return;
GameScene scene = (GameScene) AndEngineTutorial2Activity.getSharedInstance().mCurrentScene;
Bullet b = BulletPool.sharedBulletPool().obtainPoolItem();
b.sprite.setPosition(sprite.getX() + sprite.getWidth() / 2,
sprite.getY());
MoveYModifier mod = new MoveYModifier(1.5f, b.sprite.getY(),
-b.sprite.getHeight());
b.sprite.setVisible(true);
b.sprite.detachSelf();
scene.attachChild(b.sprite);
scene.bulletList.add(b);
b.sprite.registerEntityModifier(mod);
scene.bulletCount++;
}


Kiểm tra nếu Ship mà không dịch chuyển được thì bỏ qua (Vì không dịch chuyển được thì không bán đạn làm gì)

Chúng ta có được Scene hiện tại. và lấy được 1 viên đạn từ pool sau đó chúng ta thiết lập nó ở vị trí giữa bên trên Ship.
Chúng ta tạo ra 1 MoveYModifier và dịch chuyển viên đạn này dọc theo trục Y.
Trong trường hợp hiện tại của chúng ta, chúng ta cho di chuyển viên đạn từ vị trí ban đầu tới vị trí hết màn hình. Và ta cho hiển thị viên đạn này lên, và sau đó detach bản thân nó từ pool (Mặc dù khi ta lấy nó ra bằng obtain….() thì bản thân nó đã detach rồi, nhưng ở đây tôi cứ viết thêm cho nó rõ hơn).
Tiếp theo chúng ta attach viên đạn tới Scene hiện tại và đồng thời add nó tới linked list, sau đó đăng ký modifier nếu chúng ta không thực hiện điều này thì viên đạn không thể dịch chuyển được, Sau đó ta tăng biến đếm lên 1 đơn vị.

Trước khi chúng ta chạy chương trình này chúng ta cần thực hiện thêm một số bước nhỏ.
Ta trở lại GameScene và implements IOnSceneTouchListener

Trong constructor chúng ta
Mã:
 setOnSceneTouchListener(this)
//Và thêm method touch như sau:
Mã:
 
@Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
synchronized (this) {
ship.shoot();
}
return true;

}
Mỗi lần ta touch on the screen thì nó sẽ gọi shoot(), ta thực hiện hàm này ngay ở đây và không cần chuyển nó đến nới khác để xử lý.

OK đến đây khi bạn touch thì đã có bullet rồi. Nhưng chúng ta còn một công việc nữa đó là chúng ta chưa làm sạch các viên đạn (Không trả lại chúng tới pool) khi chúng tới đích vì vậy chúng ta sẽ làm tiếp việc này trước khi chúng ta kết thúc phần này.

Ta thêm vào trong GameScene method sau:
Mã:
 
public void cleaner() {
synchronized (this) {
Iterator<Bullet> it = bulletList.iterator();
while (it.hasNext()) {
Bullet b = it.next();
if (b.sprite.getY() <= -b.sprite.getHeight())
{

BulletPool.sharedBulletPool().recyclePoolItem(b);
it.remove();
continue;
}
}
}
}


Chúng ta lặp đi lặp lại trên linked list (Sử dụng 1 bộ lặp) và nếu viên đạn đi ra màn hình, chúng ta sẽ trả lại nó về pool (Tái sử dụng nó), và sau đó remove nó từ linked list và tiếp tục lặp lại.

Bây giờ chúng ta chỉ cần gọi method này, chúng ta sẽ thực hiện điều này qua GameLoopUpdateHanler bằng cách add thêm dòng lệnh sau vào onUpdate()
Mã:
 
((GameScene)AndEngineTutorial2Activity.getSharedInstance().mCurrentScene).cleaner();
OK! Đến đây bạn có thể chạy thử nhé:


Hướng dẫn tạo một Game đơn giản với AndEngine 2.0 (Phần 2)

Main Menu

Như thường lệ, một game sau khi load Splash Screen xong thì bước tiếp sẽ là hiển thì Menu cho chúng ta chọn. Trong trò này tôi sẽ hiển thị nút GAME START cho chúng ta click vào để bắt đầu chơi. Bây giờ chúng ta tạo ra 1 lớp mới đặt tên là : MainMenuScene và extends MenuScene, implements IOnMenuItemClickListener

Tiếp theo chúng ta thêm 2 biến sau vào lớp này:


Mã:

Mã:
        private AndEngineTutorial2Activity activity;
final int MENU_START = 0;
Biến MENU_START là 1 biến được sử dụng để đánh dấu, nếu bạn có nhiều nút thì tương ứng có bấy nhiêu biến flag này để đánh dấu và phân biệt giữa các nút với nhau:

Trong phương thức khởi dựng chúng ta sẽ viết đoạn code sau:


Mã:
                public MainMenuScene() {
// TODO Auto-generated constructor stub
super(AndEngineTutorial2Activity.getSharedInstance().mCamera);


activity = AndEngineTutorial2Activity.getSharedInstance();


setBackground(new Background(0.09804f, 0.6274f, 0.8784f));


IMenuItem startButton = new TextMenuItem(MENU_START, activity.mFont,
activity.getString(R.string.start),
activity.getVertexBufferObjectManager());


startButton.setPosition(mCamera.getWidth() / 2 - startButton.getWidth()
/ 2, mCamera.getHeight() / 2 - startButton.getHeight() / 2);


addMenuItem(startButton);


setOnMenuItemClickListener(this);


}

Chúng ta Send Camera hiện tại tới lớp cha cho nó tiện làm việc.
Chúng ta cũng thêm vào trong strings.xml đoạn text là "GAME START"

Mã:
<stringname="start">GAME START</string>
Vị trí của nó ở giữa màn hình, và thêm nó vào giống như 1 MenuItem, và sau đó chúng ta đăng ký Scene hiện tại giống như 1 MenuItemClickListener.


Chúng ta override method onMenuItemClicked

Mã:
       @Override
public boolean onMenuItemClicked(MenuScene pMenuScene, IMenuItem pMenuItem,
float pMenuItemLocalX, float pMenuItemLocalY) {
// TODO Auto-generated method stub
switch (pMenuItem.getID()) {
case MENU_START:
//activity.setCurrentScene(new GameScene());


return true;
default:
break;


}


return false;
}

Chúng ta chỉ cần bắt sự kiện khi button được kích. Tức là khi clicked thì trò chơi sẽ bắt đầu, trước khi test thử chúng ta mở lớp SplashScreen vào thêm vào dòng lệnh sau và đặt nó trong phuong thức loadResource(). loadResource được gọi trong constructor của lớp này.
Thông thường SplashScreen là dùng để load Resource, vì không để người sử dụng phải chờ đợi trong khi Game load resource. Trong bài Tut này chúng ta thay vì tạo ra 1 thread để load resource thì sau 2 giây chúng ta hiển thị nút Start để bắt đầu hiển thị nút Start Game cho người chơi bắt đầu chơi.

Mã:
public void loadResource(){
DelayModifier dMod=new DelayModifier(2, new IEntityModifierListener() {

@Override
public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
// TODO Auto-generated method stub

}

@Override
public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
// TODO Auto-generated method stub
activity.setCurrentScene(new MainMenuScene());
}
});
/*DelayModifier dMod= new DelayModifier(2){
@Override
protected void onModifierFinished(IEntity pItem) {
// TODO Auto-generated method stub
activity.setCurrentScene(new MainMenuScene());
}
};
*/
this.registerEntityModifier(dMod);
}



Tiếp theo chúng ta cũng thay đổi màu màn hình khi GAME START xuất hiện (Các bạn nhớ thêm loadResource() vào Constructor của lớp này nhé)
Sau 2 seconds thì sẽ chuyển sang màn hình:



SHIPS (Bệ súng để bắn - di chuyển đi di chuyển lại ở cuối screen)

Bây giờ chúng ta sẽ tạo ra 1 lớp Ships (Nó là hình chữ nhật chạy bên dưới tương tự như 1 cỗ máy để bắn đặn lên các vì sao...), trong ví dụ này chúng ta chỉ tạo ra 1 Ship, nó cũng được truy cập rất nhiều.

OK! Bây giờ chúng ta sẽ tạo ra 1 lớp Ship:


Mã:
public class Ship {
public Rectangle sprite;


public static Ship instance;


//boolean moveable;
Camera mCamera;


public static Ship getSharedInstance() {


if (instance == null)


instance = new Ship();

return instance;


}


private Ship() {


sprite = new Rectangle(0, 0, 70, 30, AndEngineTutorial2Activity.getSharedInstance()


.getVertexBufferObjectManager());


mCamera = AndEngineTutorial2Activity.getSharedInstance().mCamera;


sprite.setPosition(mCamera.getWidth() / 2 - sprite.getWidth() / 2,


mCamera.getHeight() - sprite.getHeight() - 10);
//moveable=true;
}















Sprite của lớp này sẽ là 1 Hình chữ nhật, với chiều rộng 70pixels, ngang 30 pixels, vị trí của Sprite của nó là ở giữa màn hình bên dưới của camera mà chúng ta có được từ AndEngineTutorial2Activity.
Bây giờ chúng ta cần có 1 Scene cho 1 Game thực sự. Ta gọi là lớp “GameScene
Và khai báo 2 biến:

Mã:
        public Ship ship;    
Camera mCamera;
Và constructor

Mã:
                  
public GameScene() { 
 setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
mCamera = AndEngineTutorial2Activity.getSharedInstance().mCamera;
ship = Ship.getSharedInstance();
attachChild(ship.sprite);
}
Chúng ta sử dụng màu nền game là cùng màu lúc START GAME, và sau đó khởi tạo đối tượng ship bằng cách gọi method getSharedInstance(), và sau đó ta attach Sprite của Ship này tới Scene (Ta chỉ attach Sprite không phải ship).

Bây giờ chúng ta hãy quay lại lớp: MainMenuScene và thay đổi 1 chút ở onMenuItemClicked():

Mã:
Mã:
       @Override
public boolean onMenuItemClicked(MenuScene pMenuScene, IMenuItem pMenuItem,
float pMenuItemLocalX, float pMenuItemLocalY) {
// TODO Auto-generated method stub
switch (pMenuItem.getID()) {
case MENU_START:
activity.setCurrentScene(new GameScene());//Thay đổi ở đây


return true;
default:
break;


}


return false;
}

OK! Bây giờ bạn thử chạy và sẽ thấy trên màn hình đã có Sprite.



Nào bây giờ chúng ta hãy làm dịch chuyển Sprite này:

Ở đây chúng ta sẽ làm cho Sprite này dịch chuyển bằng cách sử dụng TILT Accelerometer của thiết bị.

Chúng ta sẽ dữ speed data của Accelerometer trong lớp GameScene. Vì vậy ta thêm 1 biết float vào lớp này và gọi nó là “accelerometerSpeedX”, chúng ta sẽ sử dụng nó sau.
Bây giờ ta sẽ tạo 1 lớp mới và gọi nó là “SensorListener” implements “SensorEventListener” và add các unimplements của nó.

Mã:
public class SensorListener implements SensorEventListener{
static SensorListener instance;
GameScene scene;


public static SensorListener getSharedInstance() {

if (instance == null)
instance = new SensorListener();


return instance;

}
public SensorListener() {
instance = this;

scene = (GameScene) AndEngineTutorial2Activity.getSharedInstance().mCurrentScene;

}


@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {


}

@Override
public void onSensorChanged(SensorEvent event) {

}


}
Và bây giờ trong method onSensorChanged() chúng ta cài đặt như sau:
Mã:





@Override
public void onSensorChanged(SensorEvent event) {
synchronized (this) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
scene.accelerometerSpeedX = event.values[1];
break;
default:
break;
}
}


}
Chúng ta sử dụng synchronized để đảm bảo rằng chúng ta không có 1 concurrentModificationException (Nó giống như thread safe).

Điều này sẽ làm accelerometerSpeedX trong GameScene chứa các giá trị của gia tốc SpeedX mà chúng ta sẽ sử dụng để xác định di chuyển ship như thế nào.
Bây giờ chúng ta sẽ tạo ra 1 method để di chuyển Ship. Trong lớp Ship ta thêm phương thức sau:


Mã:
public void moveShip(float accelerometerSpeedX) {
if (!moveable)
return;
if (accelerometerSpeedX != 0) {


int lL = 0;
int rL = (int) (mCamera.getWidth() - (int) sprite.getWidth());


float newX;


// Calculate New X,Y Coordinates within Limits
if (sprite.getX() >= lL)
newX = sprite.getX() + accelerometerSpeedX;
else
newX = lL;
if (newX <= rL)
newX = sprite.getX() + accelerometerSpeedX;
else
newX = rL;


// Double Check That New X,Y Coordinates are within Limits
if (newX < lL)
newX = lL;
else if (newX > rL)
newX = rL;


sprite.setPosition(newX, sprite.getY());
}
}








Ta thêm 1 biến boolean và set = true ở constructor. Đây là 1 biến flag được sử dụng để tạm dừng Ship trong khi mọi thứ khác vẫn hoạt động.

Đối với method moveShip() nó cơ bản là nhận vào 1 accelerometer khi có thay đổi và nó dịch chuyển ship theo dữ liệu nhận vào. Nó tính toán ở đâu mà sprite nên đi (vị trí hiện tại dịch chuyển sang trái hay sang phải).

Chúng ta sẽ gọi method trên trong GameScene

Mã:
        public void moveShip() {
ship.moveShip(accelerometerSpeedX);
}
Và trước khi để nó có thể di chuyển được đi đi lại lại chúng ta phải gọi method này ở bất kỳ frame nào vì vậy chúng ta sẽ cần một IUpdateHandler. Chúng ta tạo 1 lớp GameLoopUpdateHandler và implements IUpdateHandler và sao đó add method này vào
Mã:
    @Override
public void onUpdate(float pSecondsElapsed) {
// TODO Auto-generated method stub
((GameScene)AndEngineTutorial2Activity.getSharedInstance().mCurrentScene).moveShip();


}
Về cơ bản ta lấy mCurrentScene từ AndEngineTutorial2Activity và bắt nó tới GameScene (Ở lúc này Scene hiện tại là 1 instance của GameScene) và sau đó chúng ta gọi moveShip() bên trong Scene.

Chúng ta sẽ thêm 1 chút vào constructor của GameScene như sau:

Mã:
                    AndEngineTutorial2Activity.getSharedInstance().setCurrentScene(this);
sensorManager = (SensorManager) AndEngineTutorial2Activity.getSharedInstance()

.getSystemService(BaseGameActivity.SENSOR_SERVICE);
SensorListener.getSharedInstance();
sensorManager.registerListener(SensorListener.getSharedInstance(),
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_GAME);
registerUpdateHandler(new GameLoopUpdateHandler());

Chúng ta thiết lập Scene hiện tại là GameScene hiện tại. Tôi biết rằng chúng ta đang làm điều đó khi chúng ta clicked vào nút START GAME, nhưng chỉ cài đặt nó sau khi lớp này được khởi tạo giống như Constructor kết thục thì được gọi. Chúng ta không cần thay đổi nó ở giữa) và sau đó nghe ngón accelerometer thay đổi .

Phần cuối cùng chúng ta đăng ký giống như 1 UpdateHandler();
Bây giờ bạn có thể cầm thiết bị của bạn dịch chuyển và xem nó thay đổi như thế nào.


Nếu bạn chạy = Emulator thì chuyển từ màn hình nằm ngang sang màn hình dọc, Sprite này sẽ chạy dọc từ giữa màn hình xuống cuối màn hình như hình vẽ
ảnh ban đầu:



ảnh sau khi nghiên divice


Source:
http://www.mediafire.com/?9ojc4vbxri0f5my

Hướng dẫn tạo một Game đơn giản với AndEngine 2.0 (Phần 1)

Yêu cầu:
1. Bạn phải cài đặt Java, SDK, Eclipse, ADT...........Đã có hướng dẫn nhiều google.com
2. Bạn phải add AndEngine Git Plugin vào Eclipse:
ANDROID, OPEN GLES2.0, ANDENGINE....: Hướng dẫn Add AndEngine 2.0 cho Eclipse
3. Bạn phải cấu hình lại Emulator --GPU để phục vụ cho chạy GLES 2.X
ANDROID, OPEN GLES2.0, ANDENGINE....: Chạy AndEngine trong Emulator


OK! Bây giờ việc chuẩn bị đã xong. Chúng ta bắt đầu đi từng bước để tạo ra 1 game thú vị nho nhỏ

(KẺ XÂM CHIẾM KHÔNG GIAN: <Hình minh họa> nguồn: https://en.wikipedia.org/wiki/Space_invaders
Đây không phải là toàn bộ về trò chơi nhưng chỉ minh họa một chút ít và sẽ hoàn thiện sau. Trong game này ta không sử dụng bất kỳ hình ảnh bên ngoài nào.
Ta không sử dụng Sprite (Sử dụng Sprite ưu điểm giảm bộ nhớ sử dụng), trong project này hình ảnh hơi xấu xí 1 chút.


Trước tiên đi vào bài tôi nói qua một số chức năng của các thành phần trong AndEngine một chút


  • SimpleBaseGameActivity: Là một lớp chính của game, nó chứa và quản lý 1 Engine để tạo ra SurfaceView với các nội dung của Engine sẽ được vẽ ra màn hình. Có 1 điều chính sách là 1 Engine cho 1 SimpleBaseGameActivity.
  • Engine: làm trò chơi được sử lý từng bước riêng biệt theo thời gian, nó quản lý đồng bộ hóa việc vẽ ra màn hình và updating the scene. Nó chứa các nội dung mà game của bạn có hiện thời, Có 1 Scene trên mỗi Engine trừ trường hợp SplitScreenEngines.
  • IResolutionPolicy: Một cài đặt của giao diện IResolutionPolicy là một phần của EngineOptions. Nó nói cho AndEngine làm thế nào để xử lý với các size khác nhau của màn hình thiết bị. RatioResolutionPolicy sẽ tối đa hóa SurfaceView với kích thước hạn chế của màn hình, trong khi đó nó vẫn giữ được tỉ lệ của màn hình. Nghĩa là đối tượng không bị méo mó trong SurfaceView.
  • Camera: Một Camera định nghĩa 1 khung hình chữ nhật của Scene mà được vẽ trên màn hình, thông thường có 1 Camera trên mỗi Scene trừ trường hợp SplitScreenEngines. Có lớp con cho phép phóng to, thu nhỏ vị trí của Camera.
  • Scene: Lớp Scene là 1 lớp cha (root) chứa tất cả các đối tượng mà được vẽ lên trên màn hình.
  • Entity: Là các đối tượng sẽ được vẽ bên trong Scene, các đối tượng này có thể ratation, scale....
  • Texture: Một Texture là 1 "image" trong bộ nhớ của chip đồ họa. Trong Android với chiều rộng và chiều cao của 1 texture phải là mũ của 2 ví dụ hình ảnh có kích thước là 60x128 chẳng hạn, thì texture bạn phải tạo là 64x128.
  • ITextureSource: Một cài đặt của giao diện ITextureSource giống như AssetTextureSource quản lý để load 1 ảnh vào 1 vị trí được chỉ rõ trong Texture.
  • TextureRegion: Load ảnh vào texture và cắt ra theo một khung hình chữ nhật, với kích thức bạn đặt trước. Được sử dụng trong Sprites
  • PhysicsConnector: Quản lý để cập nhật AndEngine-Shapes(giống như Rectangle,Sprites, ).....


OK! Let's start! Chúng ta tạo 1 project và đặt tên là AndEngineTutorial2 chẳng hạn và tên Activity là AndEngineTutorial2Activity. Lớp này chúng ta kế thừa từ SimpleBaseGameActivity class của AndEngine và ta xóa bỏ toản bộ nội dung bên trong lớp. Và ta cài đặt 3 method abstract của superclass bằng cách click vào unimplements trên lớp activity--> Nó sẽ tự dộng Gen ra 3 method. Tiếp theo trong Activity này chúng ta khai báo các biến như sau

Mã:
        public static final int CAMERA_WIDTH = 800;
public static final int CAMERA_HEIGHT = 480;


public Font mFont;
public Camera mCamera;


public Scene mCurrentScene;
public static AndEngineTutorial2Activity instance;
Chú ý rằng tôi sử dụng biến tĩnh cho CAMERA_WIDTH, CAMERA_HEIGHT. Việc sử dụng biến tĩnh này sẽ rất tốt vì nó phụ thuộc vào kích thước màn hình của thiết bị (tỷ lệ chiều cao, rông....) cho nên bạn không bận tâm chiều cao, rộng của màn hình thiết bị (Điều này tôi sẽ giải thích rõ phần cuối bài).


Sau đó chúng ta sử dụng Font để viết chữ lên trên màn hình giới thiệu <Việc này cũng rất cần thiết trong các trò game>
mCurrentScene là 1 đối tượng kiểu Scene. Nó được sử dụng để chuyển đến Scene trên cùng, nó rất là hữu dụng bạn sẽ biết nó hữu dụng như thế nào trong những phần sau.
Và biến cuối cùng là Activity của chính lớp này.


trong method onCreateEngineOptions() chúng ta thực hiện như sau:


Mã:
        @Override
public EngineOptions onCreateEngineOptions() {
// TODO Auto-generated method stub
instance = this;


mCamera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);


return new EngineOptions(true, ScreenOrientation.LANDSCAPE_SENSOR,
new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), mCamera);
}
Tiếp theo trong Method onCreateResources()

Mã:
        @Override
protected void onCreateResources() {
// TODO Auto-generated method stub
mFont = FontFactory.create(getFontManager(), getTextureManager(), 256,
256, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 32);
mFont.load();


}
Ở trong Game này như tôi đã nói chúng ta không cần bất kỳ Image nào. Chúng ta chỉ khởi tạo mỗi mFont và load nó.

Trong onCreateScene()

Mã:
        public void setCurrentScene(Scene scene){
mEngine.registerUpdateHandler(newFPSLogger());
mCurrentScene = newScene();
mCurrentScene.setBackground(newBackground(0.09804f, 0.7274f, 0.8f));
returnmCurrentScene;
}
OK! Đến đây coi như bước đầu tiên ta có thể chạy thử rồi, trước khi chạy thử ta chèn thêm 2 method này vào lớp Activity của chúng ta:

Mã:
        public static AndEngineTutorial2Activity getSharedInstance(){
return instance;
}
//De thay doi scene hien tai

public void setCurrentScene(Scene scene){
mCurrentScene=scene;
getEngine().setScene(scene);
}
getSharedInstance() sẽ trả về Actitvity hiện tại của lớp AndEngineTutorial2Activity vì vậy chúng ta có thể truy cập tới lớp này. Và setCurrentScene() sẽ đặt lại Scene và update mCurrentScene tới Scene mới.

OK! Bây giờ ta chạy thử chương trình, nó sẽ hiển thị màu xanh và không có gì.




Tiếp theo chúng ta cùng nghiên cứu về Splash (Nó được sử dụng rất nhiều trong các Game)

Chúng ta sẽ tạo một Splash Screen với dòng chữ chạy từ 2 phía màn hình (Mục đích để giới thiệu về game, hoặc tác giả....)

Trong strings.xml chúng ta thêm 2 dòng sau vào trong file này: Nhớ chỉnh lại trong file utf-8

Mã:
    <string name="title_1">Tran</string>
<string name="title_2">Thuan</string>
Tiếp theo chúng ta tạo ra 1 lớp mới và đặt tên là: SplashScreen và kết thừa từ Scene và sau đó chúng ta tạo ra 1 biến như sau:

Mã:
private AndEngineTutorial2Activity activity;
Bên trong phương thức khởi dụng của lớp này ta thêm vào như sau:
Mã:
                setBackground(new Background(0.09804f, 0.6274f, 0));


activity = AndEngineTutorial2Activity.getSharedInstance();


Text title1 = new Text(0, 0, activity.mFont,
activity.getString(R.string.title_1),
activity.getVertexBufferObjectManager());


Text title2 = new Text(0, 0, activity.mFont,
activity.getString(R.string.title_2),
activity.getVertexBufferObjectManager());

Bây giờ chúng ta có đối tượng Activity tham chiếu tới lớp AndEngineTutorial2Activity của chúng ta, chúng ta tạo ra 2 đối tượng Text, và chúng đều ở vị trí là 0,0.

Tiếp theo chúng ta tạo sự chuyển động cho 2 Text này như sau:

Mã:
                title1.setPosition(-title1.getWidth(), activity.mCamera.getHeight() / 2);


title2.setPosition(activity.mCamera.getWidth(),
activity.mCamera.getHeight() / 2);


attachChild(title1);
attachChild(title2);


title1.registerEntityModifier(new MoveXModifier(1, title1.getX(),
activity.mCamera.getWidth()/2 - title1.getWidth()));
title2.registerEntityModifier(new MoveXModifier(1, title2.getX(),
activity.mCamera.getWidth() / 2));


//loadResource();
Ở đây chúng ta đã thay đổi vị trí của mỗi đối tượng Text, 2 Text này sẽ chạy từ 2 bên mà hình tới gặp nhau ở giữa màn hình.

OK! Bây giờ chúng ta hãy làm nó hoạt động, ...Bạn hãy trở lại lớp Activity và thay đổi trong onCreateScene() như sau:

Mã:
       protected Scene onCreateScene() {
// TODO Auto-generated method stub
mEngine.registerUpdateHandler(new FPSLogger());
mCurrentScene = new SplashScene();
//mCurrentScene.setBackground(new Background(1,1,1));

return mCurrentScene;
}
Đến đây bạn chạy thử và sẽ nhìn thấy Text chạy từ 2 bên màn hình như trong hình. (2 chữ chạy từ 2 góc màn hình tới center)

Thứ Ba, 15 tháng 10, 2013

Game C/C++ cho newbie - 06. Frame-rate


Khái niệm


  • Frame: một vòng lặp của game gọi là một frame. Mỗi vòng lặp gồm 2 thao tác chính là update và render (đã trình bày ở các phần trước)
  • Framerate: tầng số frame, tính bằng số lần lập/số frane thực hiện trong một giây (fps). [Xem thêm]. Cũng có thể hiểu là số lần vẽ lên màn hình tính trong thời gian một giây. Thông thường với game ngưỡng thấp nhất chấp nhận được là 8 fps (frame per second) và không có ngưỡng tối đa.
  • FrameDt: khoảng thời gian dùng để thực hiện xong một frame.
  • Limit framerate: là kỹ thuật giới hạn số lượng frame trong 1 giây. Kỹ thuật này giúp:
    + Ổn định framerate chung cho cả game
    + Giảm tình trạng game lúc nhanh lúc chậm
    + Giúp đồng bộ hóa framerate của game trên các device khác nhau
    + Giảm năng lượng tiêu tốn không cần thiết. Fps càng cao, đồng nghĩa với việc CPU/GPU làm việc trong khoảng thời gian dài với công suất cao, gây hao phí không cần thiết. Limit framerate giữ fps ở mức độ vừa phải, tạo ra khoảng thời gian "nghĩ ngơi" cho CPU/GPU

Kỹ thuật Limit framerate


Như đã trình bày ở trên, Limit framerate đảm bảo fps được duy trì ổn định quanh một giá trị qui ước. Giả sử mong muốn tốc độ game ổn định ở khoảng 25 fps. 

25 fps40ms/frame

Như vậy, mỗi lần update & render trung bình khoảng 80 ms. Trong trường hợp tổng thời gian update + render nhỏ hơn 80ms, game được phép "ngủ" trong khoảng thời gian còn lại.

Trong hình minh họa trên:

  • Frame 1: tổng thời gian update + render là 20ms. Do đó, game được sleep trong khoảng 20ms còn lại
  • Frame 2: tổng thời gian update + render là 50ms > 40ms. Không cần sleep (hoặc sleep 1)
  • Frame 3: tổng thời gian update + render là 25ms. Do đó, game được sleep trong khoảng 15ms còn lại
Kỹ thuật limit fps không phải là kỹ thuật làm tăng fps. Để nâng cao fps cho game, cần kỹ thuật optimization.

Nên limit fps bao nhiêu?


Không có một giá trị cụ thể nào được đưa ra cho câu trả lời này. Tùy vào từng game, từng loại game mà người lập trình/nhà sản xuất đưa ra con số qui định cho mình. Thông thường, fps = 25 là ổn.

Tuy nhiên, việc limit fps sẽ không có tác dụng nếu fps thật sự nhỏ hơn fps cần limit. Xem ví dụ trên, frame 2. Trong trường hợp này, limit fps không có vai trò gì khi đặt ở ngưỡng 25fps. Tuy nhiên, với ngưỡng 12.5 fps (80 ms/frame), limit fps lại có tác dụng.

Implementation



  • Khai báo thư viện time.h trong header.h để sử dụng hàm clock(). Việc khai báo này được thực hiện với config PLATFORM_WIN32_VS. Các platform khác có một chút khác biệt ta sẽ đề cập sau

Header.h

1
2
3
4
5
6
7
8
//Header.h
 
....
#if CONFIG_PLATFORM==PLATFORM_WIN32_VS
#   include
#   include
#endif
....
  • Trong CDevice, thêm hàm GetTimer() trả về ngày giờ hệ thống.

CDevice.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//CDevice.h
...
namespace GameTutor
{
    class CDevice
    {
    public:
        ...
        void SleepEx(unsigned long milisec);
        unsigned long GetTimer();
        ...
    };
}
...

CDevice.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
//CDevice.cpp
......
    unsigned long CDevice::GetTimer()
    {
#if CONFIG_PLATFORM==PLATFORM_WIN32_VS
        return clock();
#else
        TODO("GetTimer for CONFIG_PLATFORM!=PLATFORM_WIN32_VS is not implement yet !");
 
        return 0;
#endif
    }
......

Bước 2: Tạo lớp CFpsManager quản lý fps


Lớp CFpsManager cũng được thiết kế dạng singleton, gồm các phương thức:

  • SetLimitFps: thiết lập thông số limit fps
  • BeginCounter: Được gọi khi bắt đầu tình toán fps
  • EndCounter: Được gọi tại ví trí kết thúc tính đoán fps và thực hiện limit frame rate
  • GetFrameDt: Lấy FrameDT hiện tại
  • GetRuntimeFps: Lấy Fps hiện tại (giá trị do đạc thực tế). Kết quả trả về có thể không trùng khớp với giá trị thiết lập bởi SetLimitFps

CFpsController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#ifndef __CFPSCONTROLLER_H__
#define __CFPSCONTROLLER_H__
 
#include "Header.h"
 
#define DEFAULT_LIMIT_FPS 25
namespace GameTutor
{
    class CFpsController
    {
    public:
        static CFpsController* GetInstance()
        {
            if (!s_pInstance)
            {
                s_pInstance = new CFpsController();
            }
            return s_pInstance;
        }
        virtual ~CFpsController(){}
         
        void SetLimitFps(unsigned int limitFps);
        void BeginCounter();
        void EndCounter();
 
        int GetFrameDt() {return m_iFrameDt;}
        int GetRuntimeFps() {return (m_iFrameDt)?int(1000/m_iFrameDt):0;}
 
    protected:
        CFpsController(): m_iLimitFps(0), m_iLimitFrameDt(0),
            m_iStartTime(0), m_iFrameDt(0)
        {
            SetLimitFps(DEFAULT_LIMIT_FPS);
        }
        static CFpsController* s_pInstance;
    protected:
        int m_iLimitFps;
        int m_iLimitFrameDt;   
        int m_iFrameDt;
    private:
        long m_iStartTime;
    };
}
 
#endif

CFpsController.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "CFpsController.h"
#include "CDevice.h"
 
namespace GameTutor
{
    CFpsController* CFpsController::s_pInstance = 0;
 
    void CFpsController::SetLimitFps(unsigned int limitFps)
    {
        m_iLimitFps = limitFps;
        m_iLimitFrameDt = 1000/limitFps;
    }
 
    void CFpsController::BeginCounter()
    {
        m_iStartTime = CDevice::GetInstance()->GetTimer();
    }
 
    void CFpsController::EndCounter()
    {
        long Endtime = CDevice::GetInstance()->GetTimer();
        int Dt = int(Endtime - m_iStartTime);
        if (Dt < m_iLimitFrameDt)
        {
            m_iFrameDt = m_iLimitFrameDt;
            CDevice::GetInstance()->SleepEx(m_iLimitFrameDt - Dt);
        }
        else
        {
            m_iFrameDt = Dt;
            CDevice::GetInstance()->SleepEx(1);
        }
         
    }
}

Ngoài cái chức năng chính trên, CFpsController còn có thể mở rộng, phục vụ cho việc thống kê fps.

Bước 3: Cài đặt chức năng tính toán fps vào vòng lập chính của game


Ta tiến hành hiệu chỉnh CGame

CGame.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "CGame.h"
#include "Header.h"
#include "CStateManagement.h"
#include "CDevice.h"
#include "CFpsController.h"
 
namespace GameTutor
{
    ...
 
    void CGame::Run()
    {
        this->Init();
        while (m_isAlived)
        {
            CFpsController::GetInstance()->BeginCounter();
            if (m_isPaused)
            {
                CStateManagement::GetInstance()->Update(true);
            }
            else
            {
                CStateManagement::GetInstance()->Update(false);
            }
            CFpsController::GetInstance()->EndCounter();
        }
 
        
         
        Destroy();
    }
}

Source code


Bài đăng phổ biến