Friday, January 15, 2010

Auto startup oracle

1, Đầu tiên bạn đăng nhập vào root và sửa file /etc/oratab: Thay đổi ký tự cuối cùng thành Y.
ORCL:/app/oracle/111:Y
Trước đó trông nó như: ORCL:/app/oracle/111:N
2, Tạo file oracle trong thư mục /etc/init.d/ với nội dung như sau:
#!/bin/bash
#
# oracle Init file for starting and stopping
# Oracle Database. Script is valid for 10g and 11g versions.
#
# chkconfig: 35 80 30
# description: Oracle Database startup script

# Source function library.

. /etc/rc.d/init.d/functions


ORACLE_OWNER="oracle"
ORACLE_HOME="/app/oracle"

case "$1" in
start)
echo -n $"Starting Oracle DB:"
su - $ORACLE_OWNER -c "$ORACLE_HOME/bin/dbstart $ORACLE_HOME"
echo "OK"
;;
stop)
echo -n $"Stopping Oracle DB:"
su - $ORACLE_OWNER -c "$ORACLE_HOME/bin/dbshut $ORACLE_HOME"
echo "OK"
;;
*)
echo $"Usage: $0 {start|stop}"
esac

3, Chạy câu lệnh:
chmod 750 /etc/init.d/oracle
chkconfig --add oracle --level 0356

Ok roài! restart thử để thấy kết quả :D!

Wednesday, January 13, 2010

Lấy địa chỉ IP của máy android của bạn!

Cũng chưa có kinh nghiệm nhiều trong lập trình android.
Nhưng mình vừa học được cách lấy ip từ máy android share cho mọi người tham khảo:
Đầu tiên bạn có thể thấy trong lớp WifiInfo có phương thức getIpAddress().
Nhưng kết quả trả về lại là một số nguyên:

WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
int ipAddress = wifiInfo.getIpAddress();

Hình như đây không phải là cách đúng.

Một cách khác cũng khá triển vọng là bạn có thể tạo một kết nối socket (socket connection) và dùng thể hiện của Socket để lấy địa chỉ ip của máy mình.
try {
    Socket socket = new Socket("www.androidvn.net", 80);
    Log.i("", socket.getLocalAddress().toString());
} catch (Exception e) {
    Log.i("", e.getMessage());
}

Nhưng cách này hình như lại có một số điểm bất lợi:
    + Bạn tạo ra một kết nối, tạo ra một lưu lượng vận chuyển dẫn đến người dùng có thể bị đánh vào cước :D.
    + Doạn chương trình này được quyết định bởi server mà bạn kết nối tới.
    + Tại đây có thể xảy ra Exception.

Tư tưởng đúng cho trường hợp này là tìm và lặp trên tất các các giao tiếp mạng và tất cả địa chỉ IP :D.

public String getLocalIpAddress() {
    try {
        for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
            NetworkInterface intf = en.nextElement();
            for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                InetAddress inetAddress = enumIpAddr.nextElement();
                if (!inetAddress.isLoopbackAddress()) {
                    return inetAddress.getHostAddress().toString();
                }
            }
        }
    } catch (SocketException ex) {
        Log.e(LOG_TAG, ex.toString());
    }
    return null;
}

Nếu hàm trả về null có nghĩa là không có kết nối nào available.
Nếu hàm trả về một String thì đó là địa chỉ ip hiện tại của thiết bị khi kết nối wifi hay 3G.

Monday, January 11, 2010

Light Racer 2.0 - Day 9 - Adding Items

After working with the AI a bit, I realized that the game feels totally dull to me. I spent a little time thinking about game design and decided to go with levels and items. Items weren't designed in to the game at all so I have to add the whole concept in from scratch. The first item going in is the Speed Boost item. When a player picks it up, their racer will move a notch faster until the end of the round. I have a problem though, the game is coded to run at the speed of the current level, not of the current player.

Here's what I did today:

Built Project Plan
Designed Icon
Implemented basic non-working SpeedBoostItem that could just draw itself
Added SpeedBoostItem to the world
Changed the way motor sounds are handled so they will work at different speeds
Modified game to allow for different speed players
Added a method incrementSpeed() to Player
Added item collision detection
Added random item placement - needs refining
Animated SpeedBoostItem

The hardest thing was adding the concept of speed on to the Player class, which is necessary to have players run at independent speeds. The problem was that all of the AI code and collision detection was using the overall game's speed to calculate distances. I had to change several methods to use the specific player's speed in the calculations, which took about an hour. After that, it was fairly straight-forward.

I drew the images of the item in photoshop:

Speed Boost Item in PhotoshopSpeed Boost Item in Photoshop 3 Image Files3 Image Files

I created the item class and added it to the world. The main game is responsible for placing the item. After that, the item is responsible for detecting a collision and responding. When it's done, it will set a flag that it is finished and the game will remove it from the world. The collision detection is very simple. Here's how it works:

private void checkCollisions(World world) {
// check to see if a player is colliding with me, do something to that player
int top = y - (int) (height * .5) - PLAYER_ITEM_TOUCH_PADDING;
int bottom = y + (int) (height * .5) + PLAYER_ITEM_TOUCH_PADDING;
int left = x - (int) (width * .5) - PLAYER_ITEM_TOUCH_PADDING;
int right = x + (int) (width * .5) + PLAYER_ITEM_TOUCH_PADDING;
Player[] players = world.players;
Player player;
int lx, ly, cx, cy;
for (int playerIndex = 0; playerIndex < players.length; playerIndex++) {
player = players[playerIndex];
lx = player.lastX;
ly = player.lastY;
cx = player.curX;
cy = player.curY;
if (cx == lx && (cx >= left && cx <= right)) {
// moving vertical within horizontal bounds
if ((ly >= bottom && cy < bottom) || (cy > top && ly <= top)) {
// collision
doCollision(player);
}
} else if (cy == ly && (cy >= top && cy <= bottom)) {
// moving horizontal within vertical bounds
if ((lx >= right && cx < right) || (cx > left && lx <= left)) {
// collision
doCollision(player);
}
}
}
}

The animation code is the same code I always use for animations

For now the game is just randomly dropping the item all over but once I have all 3 items in, I'll work out the balance of the item drop. For now it really added a new dynamic element to the game. I can see the game getting much more fun in the next few weeks!

Here's a video of the new item working.



Friday, January 8, 2010

Bắt đầu với google maps trên android

Trước tiên bạn phải đăng ký một cái maps API key của google bằng cách vào
http://code.google.com/android/maps-api-signup.html
Trước đó bạn phải kiếm cái MD5 của máy mình đã bằng cách
Kiểm tra thư mục C:Documents and Settings<UserName>.android
Của mình cụ thể là C:Documents and SettingsTanNM.android
Bạn sẽ thấy cái file debug.keystore
copy nó vào trong C:Program FilesJavajdkbin
trên dòng lệnh command bạn cd vào thư mục C:Program FilesJavajdkbin
Chạy câu lệnh:
keytool -list -alias androiddebugkey -keystore debug.keystore -storepass android -keypass android

Sẽ được một kq giống như:
   1. androiddebugkey, Oct 27, 2009, PrivateKeyEntry,
   2. Certificate fingerprint (MD5): E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3

Sau đó vào trang
http://code.google.com/android/maps-api-signup.html
điền cái mã MD5:
E1:D0:F8:0C:F7:11:F0:04:F9:F7:2F:21:49:DD:F7:B3

vào và submit, ta sẽ có một key giống như:
09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA

Ngon roài!!!!!!

Tiếp đến ta tạo một ứng dụng đơn giản trên myeclipse
file Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.ftl.map">
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
        <uses-permission android:name="android.permission.INTERNET" />
        <application android:label="@string/activity_sample_code"
               android:icon="@drawable/icon">
                <uses-library android:name="com.google.android.maps" />
                <activity android:name="com.ftl.map.view.MapViewDemo"
                       android:label="MapView">
                        <intent-filter>
                                <action android:name="android.intent.action.MAIN" />
                                <category android:name="android.intent.category.SAMPLE_CODE" />
                        </intent-filter>
                </activity>
        </application>
</manifest>

file main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/main" android:layout_width="fill_parent"
       android:layout_height="fill_parent">
        <com.google.android.maps.MapView
               android:layout_width="fill_parent" android:layout_height="fill_parent"
               android:enabled="true" android:clickable="true" android:apiKey="09BcO4uoQ0MIS9P9FU2G2MK2imfqIE5kvrJ8JRA" />
</LinearLayout>


file java Activity

package com.ftl.map.view;

import android.os.Bundle;

import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;

public class MapViewDemo extends MapActivity {

        private MapView myMapView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(com.ftl.map.R.layout.main);
        }

        @Override
        protected boolean isRouteDisplayed() {
                return true;
        }
}

Chạy nào!!!!!

Lập trình database trên android

Để các bạn lập trình được với các ứng dụng cần dùng đến cơ sở dữ liệu trên android mình xin giới thiệu với các bạn sơ qua về lập trình database trên android:
Thật tuyệt vời khi mà trên android OS đã hỗ trợ chúng ta SQLite:
1, Việc đầu tiên bạn cần làm với database là tạo một cơ sở dữ liệu: Ở đây nó chỉ là một file cơ sở dữ liệu:
private final String MY_DATABASE_NAME = "myAndroidDB";
private final String MY_DATABASE_TABLE = "Member";
SQLiteDatabase myDB = null;
try {
/* Create the Database (no Errors if it already exists) */
this.createDatabase(MY_DATABASE_NAME, 1, MODE_PRIVATE, null);



2, Bạn đã tạo database roài giờ mở nó thế nào nhỉ:

/* Open the DB and remember it */
myDB = this.openDatabase(MY_DATABASE_NAME, null);



3, Tiếp đến bạn tạo một table đơn giản:

/* Create a Table in the Database. */
myDB.execSQL("CREATE TABLE IF NOT EXISTS "
+ MY_DATABASE_TABLE
+ " (LastName VARCHAR, FirstName VARCHAR,"
+ " Country VARCHAR, Age INT(3));");



4, Giờ ta insert thử vào database xem sao nhỉ

/* Add two DataSets to the Table. */
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Nguyễn', 'Tân', 'VietNam', 24);");
myDB.execSQL("INSERT INTO "
+ MY_DATABASE_TABLE
+ " (LastName, FirstName, Country, Age)"
+ " VALUES ('Nguyễn', 'Dung', 'VietNam', 23);");



5, Có dữ liệu roài giờ truy vấn thử xem sao nhỉ:

/* Query for some results with Selection and Projection. */
Cursor c = myDB.query("SELECT FirstName,Age" +
" FROM " + MY_DATABASE_TABLE
+ " WHERE Age > 10 LIMIT 7;",
null);



6, Sử dụng hàm getColumnIndex(String) để lấy thông tin nhé:

/* Get the indices of the Columns we will need */
int firstNameColumn = c.getColumnIndex("FirstName");
int ageColumn = c.getColumnIndex("Age");

/* Check if our result was valid. */
if (c != null) {
/* Check if at least one Result was returned. */
if (c.first()) {
int i = 0;
/* Loop through all Results */
do {
i++;
/* Retrieve the values of the Entry
* the Cursor is pointing to. */
String firstName = c.getString(firstNameColumn);
int age = c.getInt(ageColumn);
/* We can also receive the Name
* of a Column by its Index.
* Makes no sense, as we already
* know the Name, but just to shwo we can Wink */
String ageColumName = c.getColumnName(ageColumn);

/* Add current Entry to results. */
results.add("" + i + ": " + firstName
+ " (" + ageColumName + ": " + age + ")");
} while (c.next());
}
}


7, Sau khi làm việc với database bạn hãy đóng nó lại nhé:

} catch (FileNotFoundException e) {
} finally {
if (myDB != null)
myDB.close();
}



8, Cuối cùng bạn lấy thông tin ra roài giờ hiển thị nó thui nhỉ:
this.setListAdapter(new ArrayAdapter(this,
android.R.layout.simple_list_item_1_small, results));
}
}

Bắt đầu với Android layout

Theo yêu cầu của mọi người topic này mình sẽ nói về layout trong android.
Layout là kiến trúc cho giao diện người dùng trong một activity, nó nắm dữ tất cả các thành phần hiển thị ra cho người dùng.
Có 2 cách để định nghĩa và khai báo layout:
* Định nghĩa các phần tử UI trong XML.
* Khởi tạo các thành phần layout lúc chạy: Bạn có thể tạo các đối tượng View và các ViewGroup trong code của bạn.

VD về định nghĩa một layout trong XML:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
             android:orientation="vertical" >
    <TextView android:id="@+id/text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="Hello, I am a Button" />
</LinearLayout>

Bạn hãy lưu nó dưới dạng file xml và để trong thu mục res/layout/, giả sử tui save nó dưới cái tên simple_layout.xml.
Giờ chúng ta sẽ load layout mà chúng ta vừa save ở trên:
Bạn có thể load layout của bạn trong hàm onCreate của Activity bằng hàm setContentView() Để nó reference đến đúng layout của bạn chúng ta phải đưa vào tham số R.layout.layout_file_name. Với VD trên ta có đoạn code load layout trong Activity như sau:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView.(R.layout.simple_layout);
}


Các tham số và vị trí của các thành phần trên layout:
+ Các đối tượng View và GroupView hỗ trợ rất nhiều các thuộc tính, có những thuộc tính chỉ riêng nó mới có (VD TextView có thuộc tính textSize). Nhưng cũng có những thuộc tính là chung bởi vì chúng được kế thừa từ các lớp cha của chúng. Có một số thuộc tính chung cho tất cả các View object bởi vì chúng được kế thừa từ những lớp View gốc (VD mọi view đều có thuộc tính id).
* Thuộc tính id: Bất kỳ View nào cũng có thuộc tính id. Để khải báo một id bạn khai báo như sau:

   1. android:id="@+id/one"


Ký tự @ để xác định nó là id, là định danh của đối tượng View. Ký tự + nghĩa là đó là một đối tượng mới cần được tạo và add vào resource của chúng ra (Trong file R.java). Khi bạn muốn referencing đến một đối tượng view khác bạn khai báo như sau:

   1. android:id="@android:id/one"


Trỏ đến đối tượng View có định danh id là "one"
Hoặc

   1. android:layout_toLeftOf="@id/one"


Nằm bên trái đối tượng View có định danh id là "one"
....
Bạn có thể khai báo một đối tượng hoàn chỉnh như sau:

<Button android:id="@+id/my_button"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/my_button_text"/>

Khởi tạo một thể hiện của nó trong code của chúng ta như sau:

Button myButton = (Button) findViewById(R.id.my_button);

Ngoài ra còn có rất nhiều các thuộc tính khác nữa cuae View như:
layout_width và layout_height
padding và paddingBottom
layout_toLeftOf và layout_toRightOf
................
Bạn có thể tham khảo thêm bằng cách mở file layout trên Eclipse rồi sử dụng khả năng auto suggestion của Eclipse để thấy được các thuộc tính của một view trong layout.

Một số layout cơ bản trong android:

a/ FrameLayout: đây là giao diện cơ bản nhất, đơn thuần chỉ là không gian trống trên ứng dụng và không có trật tự, rất cực khi các thành phần (Button, Edit,...) được sắp xếp phía trên.
b/ LinearLayout: đây là layout có định hướng dọc hoặc ngang, tức là các thành phần bên trong layout sẽ được xếp dọc từ trái qua phải hoặc từ trên xuống dưới theo chiều ngang.
c/ TableLayout: layout dang column và row, trong đây bạn phải dùng TableRow để define từng row và số column trong từng row.
d/ AbsoluteLayout: đây là dạng layout thiết kế dựa trên việc xác định toạ độ x, y của các thành phần con.
e/ RelativeLayout: lớp layout này cho phép sắp xếp các thành phần dựa trên sự tương tác với 1 thành phần nhấn định như (bên phải, trái, bên dưới so với 1 đối tượng nhất định).

Light Racer 2.0 - Days 7 and 8 - Medium AI

Developing AI for an action/puzzle game can certainly be a challenge. What is the best approach? How do I want the computer to "feel?" How hard is too hard? What algorithms will be efficient enough to work well on a mobile device? These are all questions I had to ask myself when I was planning Light Racer 2.0. Light Racer 1.0 had just one AI implementation. 2.0 has 3: Easy, Medium and Hard. Easy will be the old Light Racer 1.0 AI. I made the decision to make Medium AI use a pathfinder algorithm and always target 1 tile in front of the opponent. A* seemed like an obvious choice for this task, but once I implemented it, I started to find that the AI is still lacking.

On Day 7, I did the first A* implementation. I found one that someone else had done in Java and modified it to make it work well with Android's limitations, which basically means that the AStar class never creates new objects and is very careful about referencing members and virtuals. This worked, but I had a few problems.

Here are some screen shots with AI debugging enabled. The white line from the yellow to the blue player is the path that A* has determined is the shortest path to the target tile. This works, except when the racer turns toward the existing trail and ends up colliding with it, as you will see in the video.







The path finding algorithm doesn't take into account how long the racer is. It is basically assuming that if you're in a tile, you can move into another adjacent tile without penalty. This is causing the Ai to run the racer into walls when turning into them while too close. Also the performance is good until A* has to do a really deep search. There are a few very inefficient pieces of code in my A* implementation that I believe I can speed up.

On Day 8, I did the following:

Tweaked the A* heuristic
Did more performance optimizations (long searches still cause a bit of slow-down)
Added better opponent selection to medium ai
tweaked collision prediction for medium ai

These changes have made the Medium AI work significantly better. I was originally planning on developing the Hard AI next, but the lack of game depth has been really bothering me throughout the last few weeks of development. I decided to switch directions and build the levels and items before going any further with the AI. Since I don't have any levels or items right now, I need to start designing the code to be able to handle them. I designed a speed-boost icon in photoshop and decided that the game should have 10 levels that can be played at 3 different difficulties. More on that tomorrow.

Thursday, January 7, 2010

Light Racer 2.0 - Days 5 and 6 - Tiling the World for AI

Most of what was done on days 5 and 6 was code to lay the foundation for the new medium and hard AI. Both AIs will make use of path-finding algorithms and because of that, using tiles to approximate coordinates will be far more CPU efficient, so long as creating the tiles themselves doesn't eat too many cycles. My strategy was to have each AI player manage its own tiles relative to itself. This makes it so that the point the player is at is at the center of a tile and that the tile size can be relative to the number of pixels a racer moves between AI updates.

On day 5, I developed a method to "tile" out the board and mark up each tile with a state: Open, Closed, Invalid, Self or Target. This is essentially all the A* algorithm needs to work. I also got a nice visual debugging of the tiles working and am able to show that they are being calculated correctly but I'm having performance problems. I think AI can consume 50% of the CPU time so that would be budgeted out as follows: Game Max AI players: 3. AI updates 10 times per second, so AI total must be under 500ms for all 30 updates, so 16ms max per update. 5ms for the basic tiling may be ok but I'd like it much better if I could keep it under 2. I'm worried that A* is going to really be a CPU killer. The problem I'm having right now is that it's taking 20ms additional to do collision detection on the tiles. I believe the method I'm using may be horribly inefficient so I'm going to take a look at how to write it totally differently.

My new approach will be to search from the world instead of from the tiles. This means that I'd start by assuming all tiles are open. Iterate each player, trace the path and mark each tile the path touches. Right now each tile is checking to see if a path touches it, which means that the full-game collision detection algorithm is run for every tile, or several hundred times.

On Day 6 I tried the new approach and it worked much better. Each full-world tile update takes around 5ms now. When AI Debugging is on, the blue areas are "Open," the red are "Closed" and the regular graphics are underneath. Currently, the MediumAI (the one I'm working on) isn't actually calculating a direction to turn to so it just go straight north. That's ok because the goal of the day is to have a really fast tile generation algorithm working.

I would have posted some code but all in all it's about 150 lines of code that wouldn't really matter much without several other classes. Code like this is usually custom to the game engine so if anyone is needing help, please ask and I can help with math and a general approach.

Some of the things I dealt with were:

final int minMovement = (world.movementRate / LightRacerConstants.AI_FPS);
final int centerOffset = minMovement / 2;
final int firstX = (player.curX + centerOffset) % minMovement;
final int firstY = (player.curY + centerOffset) % minMovement;
AITile[][] tiles = new AITile[tileCountX][tileCountY];
for (int x = 0; x < y =" 0;">

AI_FPS is a constant, set at 10, so that the AI is updated at most every 100ms. The movementRate of the world is how fast the game is playing with a number like 100 pixels per second. This would mean that dividing 100/10 would give us 10 pixels per update, which is the minimum number of pixels the physics will move a racer before the next AI update. I'm using that for the size of the tiles.

centerOffset is simply how many pixels it is to the center of a tile.

firstX is the number of pixels offset of the left wall.
firstY is the number of pixels offset of the top wall.

Since the tiles are always moved to be centered on the AI player in question, they don't usually line up perfectly with 0, 0. Often times the rows and columns around the border are smaller than the center tiles. This makes calculations more reliable because we know that in 5 updates, the racer will probably end up close to the center of a tile 5 tiles away. If we had the tiles set statically, the racer might start 1 pixel off of one tile and end up 7 pixels in to another. That's not good for determining a sound path. The flip side is that other racers and trails end up marginally in some tiles. More on that later.

After that code, I have it iterate the full x and y axis, calculating the bounds for each Tile. Right now, the AITile class looks like this:

public class AITile {
public static final int OPEN = 0;
public static final int CLOSED = 1;
public static final int INVALID = 2;

public int state = OPEN;
public int topLeftX;
public int topLeftY;
public int width;
public int height;
public int tileX;
public int tileY;
}

Wednesday, January 6, 2010

Enabling the PHP OCI8 Extension on Linux

To install on Linux, PHP generally needs to be recompiled. For users of Oracle Enterprise Linux, pre-built RPM packages of PHP and OCI8 are available from oss.oracle.com, or via ULN updates.

To build PHP and OCI8:

1.

Download the Apache HTTPD Server if you decide not to use your default package. Download the PHP 5.2 source code.

Install PHP and Apache following Installation on Unix systems in the PHP manual.

At this stage, don't configure the OCI8 extension.

Check that PHP is working before continuing.
2.

Download the Basic and the SDK Instant Client packages from the OTN Instant Client page. Either the zip file or RPMs can be used.

Install the RPMs as the root user:

rpm -Uvh oracle-instantclient11.1-basic-11.1.0.7.0-1.i386.rpm
rpm -Uvh oracle-instantclient11.1-devel-11.1.0.7.0-1.i386.rpm

The first RPM puts the Oracle libraries in /usr/lib/oracle/11.1/client/lib and the second creates headers in /usr/include/oracle/11.1/client

If using the zip files, the SDK should unzipped to the same directory as the basic package, and a symbolic link created:

ln -s libclntsh.so.11.1 libclntsh.so

3.

The latest OCI8 1.3 extension from PECL supercedes the default version in the PHP 5.2 source code. This can be downloaded manually and installed with:

pecl install oci8-1.3.5.tgz

Or the latest production package can be automatically downloaded and installed:

pecl install oci8

This gives:

downloading oci8-1.3.5.tgz ...
Starting to download oci8-1.3.5.tgz (137,987 bytes)
.....done: 137,987 bytes
10 source files, building
running: phpize
Configuring for:
PHP Api Version: 20041225
Zend Module Api No: 20060613
Zend Extension Api No: 220060519
1. Please provide the path to the ORACLE_HOME directory.
Use 'instantclient,/path/to/instant/client/lib' if you're compiling with Oracle Instant Client : autodetect

1-1, 'all', 'abort', or Enter to continue:

If you have the Instant Client RPMs, hit Enter and PECL will automatically locate the RPMs and build and install an oci8.so shared library.

If you have the Instant Client zip files, or have multiple versions of the Instant Client RPMs installed then explicitly give the path:
*

Enter 1 to update the first (and only) setting. The OCI8 configuration prompt will then be shown:

Please provide the path to the ORACLE_HOME directory. Use 'instantclient,/path/to/instant/client/lib'
if you're compiling with Oracle Instant Client [autodetect] :

*

Enter the path, such as:

instantclient,/usr/lib/oracle/11.1/client/lib

or:

instantclient,/your/path/to/instantclient_11_1

Do not use any environment variable names, because the PECL installer doesn't expand variables.
*

You will get reprompted 1-1, 'all', 'abort', or Enter to continue. As it says, press Enter to continue. PECL will build and install an oci8.so shared library.
4.

Edit php.ini and confirm extension_dir points to the directory the oci8.so file was installed into.

Also in php.ini, enable the OCI8 extension with:

extension=oci8.so

5.

Add the directory with Instant Client to /etc/ld.so.conf, or manually set LD_LIBRARY_PATH to /usr/lib/oracle/11.1/client/lib and restart Apache.

It is important to set all Oracle environment variables before starting Apache. A script helps do that:

#!/bin/sh

LD_LIBRARY_PATH=/usr/lib/oracle/11.1/client/lib:${LD_LIBRARY_PATH}
export LD_LIBRARY_PATH

echo Starting Apache
/usr/sbin/apachectl start

This script can contain desired Oracle globalization language environment variables such as NLS_LANG. If nothing is set, a default local environment will be assumed. See An Overview on Globalizing Oracle PHP Applications for more details.

Verifying the Extension is Installed

To check the extension is configured, create a simple PHP script phpinfo.php in the Apache document root:



Load the script into a browser using the appropriate URL, e.g. "http://localhost/phpinfo.php". The browser page will contain an "oci8" section saying "OCI8 Support enabled".

The OCI8 options that can be configured in your php.ini file are shown.
Connecting to Oracle

Oracle authentication and database information is passed to oci_connect() to create a connection. Tools linked with Instant Client are always "remote" from any database server and an Oracle Net connection identifier must be used along with a username and password. The connection information is likely to be well known for established Oracle databases. With new systems the information is given by the Oracle installation program when the database is set up. The installer should have configured Oracle Net and created a service name.
There are several ways to pass the connection information to PHP. This example uses Oracle's Easy Connect syntax to connect to the HR schema in the MYDB database service running on mymachine. No tnsnames.ora or other Oracle Network file is needed:

$c = oci_connect('hr', 'hr_password', '//mymachine.mydomain/MYDB');

See Oracle's Using the Easy Connect Naming Method documentation for the Easy Connect syntax.

In new databases the demonstration schemas such as the HR user may need to be unlocked and given a password. This may be done in SQL*Plus by connecting as the SYSTEM user and executing the statement:

ALTER USER username IDENTIFIED BY new_password ACCOUNT UNLOCK;

Using Oracle

Try out a simple script, testoci.php. Modify the connection details to suit your database and load it in a browser. This example lists all tables owned by the user HR:

\n";
}

oci_free_statement($stid);
oci_close($conn);

?>

Troubleshooting

Check the Apache error log file for startup errors.

Temporarily set display_error=On in php.ini so script errors are displayed.

Chapter 9 of The Underground PHP and Oracle Manual contains some common connection errors.

Oracle's SQL*Plus command line tool can be downloaded from the Instant Client page to help resolve environment and connection problems.

Check the environment used by SQL*Plus is the same as shown by phpinfo.php.

Windows Help

If the phpinfo.php script does not produce an "oci8" section saying "OCI8 Support enabled", verify that extension=php_oci8_11g.dll is uncommented in php.ini.

If PATH is set incorrectly or the Oracle libraries cannot be found, starting Apache will give an alert: "The dynamic link library OCI.dll could not be found in the specified path." The Environment section of the phpinfo() page will show the values of PATH and the Oracle variables actually being used by PHP.

If php.ini's extension_dir directive is not correct, Apache startup will give an alert: "PHP Startup: Unable to load dynamic library php_oci8.dll."

Linux Help

If using Instant Client zip files, make sure the two packages are unzipped at the same time. Make sure a symbolic link libclntsh.so points to libclntsh.so.11.1

Set all required Oracle environment variables in the shell that starts Apache.

Installing Oracle, PHP, and Apache on Linux

Let's walk through the steps required to install the Oracle Database, Apache HTTP Server, and PHP as an Apache module on Linux. Check out the OTN Linux Technology Center to see what versions of Linux are certified for use with the Oracle Database. We will be using Oracle Enterpise Linux for this example.

Software Requirements:

Software Version URL
Oracle Database 10g Express Edition 10.2 http://www.oracle.com/technology/products/database/xe/
Apache HTTP Server 2.0 http://httpd.apache.org/download.cgi
PHP Hypertext Processor 5.2 http://www.php.net/downloads/

Installing Oracle

You have a choice here. You may either install the database locally on this Linux machine, or you may decide to use an Oracle server located on another machine on your network. If your database is remote, jump to the article on Installing PHP and the Oracle Instant Client for Linux and Windows.

Otherwise, if this is your first time with Oracle, installing the Oracle Database 10g Express Edition only takes a few minutes. Download the Express Edition (commonly known as "XE") RPM package, log in as root and run:

    # rpm -ivh oracle-xe-univ-10.2.0.1-1.0.i386.rpm

After the software package is installed on the machine, configure a database by running this and answering its four questions:

    # /etc/init.d/oracle-xe configure

For Debian users, a .deb file is also available.

Starting and Stopping Oracle

Oracle XE will be running after installation. You can test it by opening your browser to the Database home page http://localhost:8080/apex. Use the username "SYSTEM" and the password you chose during installation.

Note: You may need to replace "localhost" with the IP address 127.0.0.1 or your machine's DNS name if you are behind a firewall or if localhost does not resolve for some other reason.

If you need to restart the database at any time use the Start Database and Stop Database items on the operating system's "Oracle Database 10g Express Edition" menu. To run these you will need to add yourself to the operating system "dba" group and re-login to the machine.

Alternatively you can call the oracle-xe script as the root user:

    # /etc/init.d/oracle-xe stop

To restart:

    # /etc/init.d/oracle-xe start

Installing Apache HTTP Server

Now that the Oracle Database is installed, you should install Apache. You must install Apache before you can install PHP, since PHP will be installed into Apache.

If you don't want to mess about compiling Apache or PHP, Oracle has pre-built PHP RPM packages that use the default Apache package.

If your operating system doesn't already have Apache, download httpd-2.0.63.tar.bz2 from the Apache web site, log in as the root user and execute these commands:
    # tar -jxvf httpd-2.0.58.tar.bz2

# cd httpd-2.0.58
# ./configure --prefix=/usr/local/apache --enable-module=so
# make
# make install

When configuring the web server, the option "--enable-module=so" allows PHP to be compiled as a Dynamic Shared Object (DSO). Also, the "--prefix=" option sets where Apache will be installed during the command "make install"

If you are familiar with the tar command on UNIX systems, you may be wondering why we did not need to invoke bunzip2 to extract the tar file. Linux includes the GNU version of tar which has a new 'j' flag to automatically uncompress a bzipped tar file. If you downloaded the gzipped file you could have used the 'z' flag instead.

Note: With Apache 2 you should use the default pre-fork MPM ("Multi-Processing Module") because many of the PHP extentions are not known to be thread-safe.

Starting and Stopping Apache

Start and stop Apache with the apachectl script:

    # /usr/local/apache/bin/apachectl start

You should test that Apache is up and running on your machine by opening your web browser to http://localhost/.

Now stop Apache so it can be configured for PHP:

    # /usr/local/apache/bin/apachectl stop

On Oracle Enterprise Linux, the apachectl script for the default Apache package is /usr/sbin/apachectl.

Installing PHP

To build PHP yourself, download the file php-5.2.9.tar.bz2 from the PHP downloads page.

Installation Steps

  1. Log in as the root user and execute these commands:

        # tar -jxvf php-5.2.9.tar.bz2
    
    # cd php-5.2.9
    # export ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
    # ./configure \
    --with-oci8=$ORACLE_HOME \
    --with-apxs2=/usr/local/apache/bin/apxs \
    --with-config-file-path=/usr/local/apache/conf \
    --enable-sigchild
    # make
    # make install

    Note: if you are behind a firewall, you may need to set the environment variable http_proxy to your proxy server before running make install. This enables PHP's PEAR components to be installed.

  2. Copy PHP's supplied initialization file:

        # cp php.ini-recommended /usr/local/apache/conf/php.ini
    

    For testing it is helpful to edit php.ini and set display_errors to On so you see any problems in your code.

  3. Edit Apache's configuration file /usr/local/apache/conf/httpd.conf and add the following lines:

        #
    
    # This next section will call PHP for .php, .phtml, and .phps files
    #
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php .phtml
    AddType application/x-httpd-php-source .phps

    #
    # This is the directory containing php.ini
    #
    PHPIniDir "/usr/local/apache/conf"

    If a LoadModule line was not already inserted by the PHP install, add it too:

        LoadModule php5_module modules/libphp5.so
    

Restart the Apache HTTP Server

You must now restart the Apache Server so that you can test your PHP installation.

    # /usr/local/apache/bin/apachectl start

Note: If you are using Oracle 10.2 but not the Express Edition, you must give the "nobody" user access to the Oracle directory. With Oracle 10.2.0.2 there is a script $ORACLE_HOME/install/changePerm.sh to do this.

If there are errors, they will display on your screen. They may also be recorded in /usr/local/apache/logs/error_log. If you have problems, double check your httpd.conf and php.ini, and make corrections.

When you start Apache, you must at least have ORACLE_HOME defined. Any other required Oracle environment variables must be set before Apache starts too. These are the same variables set by the $ORACLE_HOME/bin/oracle_env.sh or the /usr/local/bin/oraenv scripts.

To simplify things, you may create a script to start Apache. I did this and named it start_apache:

    #!/bin/sh


ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server
export ORACLE_HOME
echo "Oracle Home: $ORACLE_HOME"
echo Starting Apache
/usr/local/apache/bin/apachectl start
Testing Apache and PHP with Oracle

Testing PHP with Oracle is easy. You simply need to place a PHP file into your htdocs directory; normally /usr/local/apache/htdocs.

Here are two files, the first is used to test basic PHP installation. Open it in a browser with http://localhost/phpinfo.php. If PHP is installed you should see a large page full of PHP configuration information.

phpinfo.php
   

Check there is a section titled "oci8".

oci8test.php

The second file will display name and salary columns from the EMPLOYEES table owned by the HR user. This requires the HR schema be installed otherwise you will need to modify the script. The HR schema comes with Oracle XE. You can unlock access and set a password using the Adminstration section of the Database home page.

For Oracle XE the database connection string is simply "127.0.0.1/XE". If you are not using Oracle XE then change the connection string (third parameter) to the Oracle Net entry for your database.

  Oracle PHP Test";

echo "

Oracle PHP Test


";
echo "\n\n";
echo "\n\n\n";

for ($i = 0; $i < $nrows; $i++ ) { echo "\n";
echo "";
echo "";
echo "\n";
}

echo "
NameSalary
" . $results["LAST_NAME"][$i] . "$" . number_format($results["SALARY"][$i], 2). "
Number of Rows: $nrows
";
echo "
If you see data, then it works!
\n";

?>

Cấu hình gửi mail cho joomla sử dụng gmail

Mail Settings
Mailer: SMTP Server Mail from: email@mydomain.com
From Name: Nguyễn Minh Tân
Sendmail Path: /usr/sbin/sendmail
SMTP Authentication: Yes
SMTP Security: None
SMTP Port: 25
SMTP Username: email@mydomain.com
SMTP Password: **
SMTP Host: ssl://smtp.gmail.com:465

Light Racer 2.0 - Days 3 and 4 - New Design Completion

Much like the first two days of work, days 3 and 4 involved mostly refactoring the existing code to bring it all in to the new architecture. The end of the fourth day was much more exciting than the rest because nearly 100% of the design was completed and I was able to work on new code again. I implemented 2D coordinate interpolation for the racer physics and AI, which I had a few problems with but overcame. I also implemented visual AI debugging which is key in developing better AI.

Before I started modifying the game or adding anything, I was almost purely refactoring. This approach has paid off because now I have a great new design that will allow me to add all of the new features and the game is still in a 100% usable state. In fact, I could still package it up and put it on the market with little effort. I keep pushing this idea because I believe it's very fundamental to a successful developer. Notice how through the days of refactoring, I was able to keep the game working exactly as it had before. That's the big difference between refactoring and rewriting. Call it semantics if you wish, but I have not lost a single piece of functionality and I've been able to test it several times every day and verify that everything is still working exactly the way it was when I started the project.

The great thing about what I'm doing is that I'm so confident the entire way. I'm not scared that I'm going to make a huge mess that will be a major headache to clean up. It's one thing at a time and by dealing with only one small issue at a time as I go, I'm able to tackle every issue that comes up. In software, it's simply overwhelming to deal with tons of issues all in one go. Slow and steady wins the race here, no pun intended. At the end of the refactoring, the new design was fully implemented and the game was still working exactly as I left it. Now it all works and I'll be able to add in all of the new features I want. How cool is that?

Day 3: What was completed?

The LightRacerWorld is now used consistently and several things were added to the world. It has game state information, the pixel size of the game area of the screen, all of the players, the last, current and delta tick time, the level and the current movement rate (game speed). This was important because the world is the part that is passed into GameObject update() methods, which in turn will run AI algorithms. All of these things are needed for AI to process and for physics to interpolate.

I moved SoundManager into resources. This made it so that GameObjects can trigger and control their own sounds.

I combined AI and player physics updates into a single update. This improved the interface from the main game class to the players. Now the main loop just iterates through players and calls update(world) on each. Before it had some logic to determine if the player was AI and what to do.

I moved path updates into Player. This is just to further simplify the main game loop. The more simple, the better. Here's some game testing from the end of Day 3, just to confirm that the core game was still working correctly.

Day 4: What was completed?

Player now controls all of its own sounds. This wasn't complete before but now is 100%.

Player has one update() method. It was many updates before but all of that logic exists only in the Player class and the main loop has a very easy interface to update the Players.

Changed Receiver to PlayerInputSource. I renamed that class because I think that it is more appropriate this way. The PlayerInputSource is fundamental because it is either a network client, network host, human player or AI player. It is the keystone that will allow for different kinds of AI and network configurations.

LightRacerAI now implements PlayerInputSource. It's official. The AI is now a PlayerInputSource.

Created HumanInputSource. This was to finish implementing that part of the design.

Renamed PlayerPhysics to Collisions. All that was in the class was a method to check for collisions. I made that public and static, so it's really just a utility class now. I don't normally do that but it was needed by a few different classes and didn't fit well into the hierarchy.

GameObject created but can't standardize draw methods because of layering. I have a GameObject now but because this game is 2D, I can't have one draw method. Drawing order must occur like this: Background, light paths, racers, explosions, text. If I were to do one player at a time instead of one layer at a time for all players, you would end up with paths and racers on top of explosions, which would not look good at all. I don't have a good solution for this yet but it's not the worst thing ever to have one custom interface and everything else is looking very good.

Refactoring is complete! Now for the fun stuff...

Implementing Interpolation in a 2D World
I changed the physics of the game to use time distance interpolation instead of moving a set number of pixels per tic. The idea is that the game will run the exact same speed but at different FPS depending on how much load is already on the device. I found that for different phones with different numbers of services running, the game would run faster or slower and I wanted it to be consistent.

The interpolation works really well. It's really straight forward to write interpolation for a 3D coordinate system because you can actually move a floating point number away but in 2D land there are a set number of pixels and so you must move a nice round number. This was problematic at first because when I set the movement rate to 100 pixels per second, I ended up getting about 80. Why do you think that is?

I almost knew immediately what the problem was. Interpolation is calculated by using the the last known location of the object, the direction the object is moving in, the rate of movement and the last tic time and the current tick time. If you have that information, you simply take the difference in time, multiply that against the rate of movement and then move the object that far from the last position in the direction it is moving. The formula could be expressed as newPosition = oldPosition + (tickDelta * movementRate * direction).

Here is an example that also shows the problem:

Movement Rate = 100 pixels per second
Racer is located at (x,y) (100,100) and is facing North.
The last tic time was at 1000
The current tic time is 1078
Where should he be now?

The math looks like this: 78 (tick delta in ms) * movementRate (in seconds) / 1000 (to convert seconds to milliseconds) = 7 pixels.

So we should move 7 pixels in the north direction (subtract from Y axis) so that would put our racer at 100, 93. That works, but with a tic delta of 78, how did we end up with 7 pixels? What happened to the other .8 pixels? In a floating point world, we would have moved the racer 7.8 pixels. Since we can't do that we can either discard the remainder, which is what I was doing and was causing the game to run slower than it should have, or we can use a trick that I came up with.

Since there is usually a remainder, I changed the code so that it works like this:
movement = (tickDelta * movementRate + carryOver) / 1000
carryOver = (tickDelta * movementRate + carryOver) % 1000

carryOver must be an instance field on the GameObject you are interpolating, because it will need to be the remainder from the last physics update. This worked, because now that .8 pixels from the example is carried to the next update, and if that next update has a remainder that is at least .2 pixels, then an entire pixel will be added to the movement, thus making the movements add up to 100 pixels per second.

This worked ok but I was able to see the little jumps sometimes if there were small remainders for a few updates and then they finally added up to 1000 to add a pixel. I realized that only rounding down wasn't the most even way of approaching the movement and remainder problem so I also implemented a negative carry over for remainders over 500. Now the carry over value will, instead of being 0 to 900, be -400 to 500. This worked to smooth out the 2D interpolation. Here's some physics code from the game:

long targetMovementAmount = (world.tickDelta * world.movementRate) + movementCarryOver;
int pixelsToMove = (int) (targetMovementAmount / 1000);
movementCarryOver = (int) (targetMovementAmount % 1000);
if (movementCarryOver > 500) {
// round up and carry over a debt.
movementCarryOver = movementCarryOver - 1000;
pixelsToMove++;
}

All said and done, the game runs really well now. It's much smoother than before and it doesn't feel like there's any lag even when the phone slows down for a brief second.

Making AI work consistently in a variable update-time environment
After I added the interpolation to the Player physics, I got rid of the old cap I had placed on updates. Now the game will update as fast as the SurfaceView will allow for draws, so 60FPS. My emulator runs at around 45FPS so that's not too bad. I haven't tried it on the phone yet with my FPS text turned on but I'll have more on performance later in the book. For now, a new problem has come up. I used to run the game at a set 30 updates per second (or slower) and my AI was designed to run at that speed. The new AI has to be able to also interpolate or it runs totally different on one system to the next and also it takes up far more CPU cycles than are necessary. AI doesn't actually need to recalculate 45 times per second. 10 will do just fine. Is it even fair to play a computer player that can process everything faster than your eye registers motion?

I updated the AI to use interpolation similar to that of the Player. All the AI really needs to know is "How far will the racer move until I get to make another decision?" For that I decided to combine that with an AI update limit of 10 updates per second. This made the math simple: If the AI updates every 100 milliseconds, then the furthest the racers could go before getting to make another decision is 1/10th of the current movement rate. This doesn't take into account if the game runs slower than 10FPS, in which case there will be a problem here but I'm assuming that it is unplayable at that speed anyway. It works this way because the AI needs to be able to make sure that it's not going to run into a wall before getting to decide to turn again. That number is how many pixels it will go so it can pipe that through the collision detection code and see if there is something in the way.

Limiting the AI to 10FPS is a great thing because it works well and now I have some extra CPU cycles to use for even better AI. My Medium and Hard AI are going to be far more CPU intensive than the current Easy implementation. I knew I was going to need a better way to debug the algorithms so I added in a visual AI debugging feature. The PlayerAI interface has a drawDebug(canvas) method which is called by the main loop when the AI debugging is turned on. I implemented a little bit of it for the EasyAI to make sure it was all working. The EasyAI sometimes decides to "come after" a human opponent. Each time it makes this decision, I had it draw a "targeting" line that you can see in red in this screenshot.


Light Racer Easy AI debugging

Light Racer Easy AI debugging

It looks really cool to watch it make the decisions real-time, which is in the video below.

That's it for today. There have been so many changes and I'm getting really excited about adding all of the new features.

Tuesday, January 5, 2010

Light Racer 2.0 - Days 1 and 2 - Refactoring

The first two days of actual coding have involved mostly refactoring. If you're curious as to why I'm doing all of this refactoring, I will recap on the new requirements which involve multiple kinds of AI, a new game object that I call the "decimator" and real-time multiplayer functionality. Many would think that the only way to do that would be to rewrite the game to support this, so why am I opting for refactoring to start? Some would argue that it's semantics but for the most part, refactoring is taking existing code without changing functionality and rewriting is completely scrapping a functional piece of code and developing it from scratch. Lots of the code in Light Racer was really good but just not organized to allow for new features to be added in without too much headache.

What changed?

I first started by creating two new activites: One for the game and one for the instructions. Before it was just one activity that had a hacked up layout. That hacked layout had all 3 different layouts all layered on top of each other and the activity would switch which ones were visible depending on the state of the whole game. This was very hard to manage and work on and certainly wouldn't have worked out well to add in the new multiplayer menus. Now there are three distinct Activities. One for the main menu, one for the instructions and one for the game itself. In the future there will be more to handle more of the new functionality.

My next goal was to break out the existing AI code so that I could make it more modular. What I wanted to do was be able to just pop a new instance of a MediumAI and be able to set that on any player. Here's the idea:

public void setupGame() {
Player p1 = new Player();
p1.isHuman = true;
Player p2 = new Player();
p2.setAI(new EasyAI());
Player p3 = new Player();
p3.setAI(new MediumAI());
startGame();
}

Sounds easy enough, right? Well that interface is in place now so I can have code that works almost exactly like that, but the hard part was working it out so that the AI code could work correctly on its own, which means working without access to all of the fields of the game. This was achieved by doing two things. First, I created a "World" class that contains everything that matters to the game. I moved all of the Players and state info into the World. Then I took the old updateAI() method which used to exist in the big monolithic game class and moved it into the AI interface, which all AI implementations implement, and added it as a parameter, so that every time the AI is updated, it has access to the entire world. To translate - the AI code is handed the positions of every player and game object as well as the paths of light, just like you see on the screen. This is necessary because those things are all used in the calculations for the AI to determine where to move its player to next. The most used method in that calculation is collision detection, which is some code that simply checks to see if the racer is going to hit a wall. That collision detection code is needed by AI and by the update() method in the Player class so I took a standard OO approach and created a base class called PlayerPhysics and made EasyAI and Player both extend it, thus allowing for them to share that common collision detection code.

Does this sound like a big jumble? It's actually really clean now. Here's how it looks:

public interface AI {
public void update(LightRacerWorld world);
}

public abstract class PlayerPhysics {
public boolean checkCollision(LightRacerWorld world, int fromX, int fromY, int toX, int toY) {
... collision detection code
}
}

public class EasyAI implements AI extends PlayerPhysics {
public void update(LightRacerWorld world) {
... all the old AI code is in here now
}
}

public class Player extends PlayerPhysics {
... tons of fields for position, next move, animation, color, etc
public void setAI(AI ai) {
this.ai = ai;
}

public void updateAI(LightRacerWorld world) {
ai.update(world);
}
}

Since a good, safe refactoring job moves things in small steps and doesn't break anything, this should be considered a few steps taken but not a finished job. The design in the architecture document uses Receivers, which we don't have implemented yet. For now, this is an improvement and takes steps to get there.

The other thing that was done is that resources were moved into their own class, also following the architecture document. This was necessary to allow for GameObjects to be able to draw themselves, which was an ability they did not previously possess. Did I mention I added GameObjects?

So far so good on the development. Here are screenshots of where the game is at now. Much of this work won't have any visible artifact but soon we'll get there. In fact, all that has changed aesthetically is that the game is now full screen and there are placeholders up on top for the game difficulty and level.


Main Menu Dev Shot
Main Menu Dev Shot

Light Racer 2.0 Dev Shot Game 4-7-2009
Light Racer 2.0 Dev Shot Game 4-7-2009

Light Racer's New Game Architecture

Version 1.0 of Light Racer didn't have much to say for architecture. There was a single Activity which dealt with the buttons, showed instructions and displayed the main game View. I'll cover the way the menus are going to work in a different article but suffice to say for now that having a single Activity deal with multiple different things is not a good idea at all in Android. The main View was a SurfaceView and simply started up the main game thread. The Thread class was the main game and besides the Sound Manager and a very light Player class which held the state, path and animation information for any given player, there wasn't a whole lot to it. This made it easy to develop but the new requirements are to have NPCs (Non Player Characters), network multi-player, different 2D and 3D displays and more. How does one tackle such a problem? With a better design, of course.

The new design is much more organized and also more abstract. The following slides will show how I am changing the design to delegate far more responsibility into the Game Objects and Receivers and away from the main class. The main class's job will be simply to initialize the environment, load resources, initialize and configure the world, run the game and watch for changes to the Android environment.

The environment consists of the world (everything a game cares about and will display to the user), various managers like sound and notification managers, any object pools (because Android gets slow if you are creating and GCing lots of objects) and hooks into the Android environment.

Resources are things like graphics, sounds, fonts, and colors. Many parts of the game need to access the resource pools so it's always a good idea to have them managed by one object and pass that one object to anything requiring resources. For example, all GameObjects are responsible for drawing themselves so they will need to get their bitmaps from the resource manager. The main game class will have the resource manager release its resources when it's time to clean the game environment up.

Main Class, World and ResourcesMain Class, World and Resources

The next diagram is a bit more concrete and recognizable. Player is the only Game Object on here but you can imagine that there might be others, such as a barrier or a power-up item. All of these things are Game Objects and are responsible for drawing and animating themselves. The Player will be doing collision detection on itself to check for a crash. If I end up adding power-ups, it will also check to see if it has touched or passed over an item. A Player maintains its own position in the world. It also maintains it's state so it knows if it's still alive or crashed. The path is the trail of light. Animation means which frame of what kind of animation is currently being drawn.

Game Objects, Players, Receivers and AIGame Objects, Players, Receivers and AI

The big key element in this new design is the Receiver. I couldn't think of a better name at the time and this may be renamed when I do, but a Receiver is responsible for determining the next direction of the player. In LR 1.0, the Android button hooks were tied directly to a field that held the next direction of movement until the physics update could pick it up. It also had the AI code in the same big game class and assumed that any player that was not Player 1 was AI. That won't work for the new requirements so I'm designing this to make 2 policies: 1) The Receiver is entirely responsible for setting the next direction of a Player. 2) If there is no Receiver, the Player shall always continue (interpolate) on its trajectory.

Does policy 2 make sense? When would we not want a Receiver? Wouldn't that player always end up in the wall if it started North and continued without change?

Let me start by answering that there are several types of Receivers. The Player Receiver is directly tied to the buttons and touch. When a player hits up, that receiver sets the next direction to North. Simple enough. The AI Receiver is abstract and so there can be several different AI implementations. I'm doing 3, which are Easy, Medium and Hard. The different implementations may be better at playing than one another but they all end up outputting only one thing: The next direction for their Player. The network receivers are the most confusing. I'll explain the way multi-player games will work and then get back to them.

In Light Racer, you will be able to host a game or join a game. There can be up to 3 players for the regular game and 4 players for LR3D. The clients each "estimate" what is happening but the host is the real authority. One could say that the actual, authoritative game runs on the host. This means that when the host decides there was a crash, there was a crash. This is an easy strategy to follow because the rule is that the host's world always overrides anything on the client.

Let's discuss the following example:

3 Player Network Example3 Player Network Example

In this example, there are 3 players. One player is the host of the game, the second is a client and the third is an AI player. The configurations are different in that the host uses a Human Receiver for the person playing, a NetHost Receiver to pick up the input from the remote player and an AI Receiver to control the AI. Every time it ticks and updates the world, it packages the world up and sends it to the client. The client unpackages the world and overwrites its current world. This is because the host is always the authority. The client then processes input from its one Receiver, which is a NetClient Receiver. The NetClient Receiver is dual-purpose. It has a Human Receiver so it can take button presses and touch events from the real player and that will set the next direction, but then it also immediately sends that over to the host via the network connection.

If you can imagine that happening around 30 times per second, you can see why the Client World does not need Receivers. Its World is always being updated by the host and the Players in that world are located and on the trajectory that the World dictates. This means that the client simply tells the host what it would like to do and draws what it thinks is happening. The host processes this and tells the client what actually happened. So long as the updates are fast and consistent, this will work. Even if there are small stutters, the interpolation code will take care of it and things will still look mostly smooth.

Getting Started in Android Game Development

If you're interested in developing a game for the Android platform, there is a lot you need to know. I'm the developer of Light Racer, Light Racer 3D and Wixel, which are currently available on the Android Market. I've developed games before but the original Light Racer was my first Android application and I learned quite a bit about writing Android games that I'd like to share with everyone. I even wrote an online book detailing the development of Light Racer 3D, which is full of how-tos and useful code snippets. If you have previous experience with game development, moving over to the mobile platform won't be all that difficult. You will mostly just need to learn the architecture and API. If you're new to game development, I have assembled a list of must-knows for getting started. They apply to many different types of games, including action, strategy, simulation and puzzle.

Android is a Java-based environment. This is nice for new developers as Java is widely accepted as a much easier language to get started in than C++, which is the norm for mobile development. Google has also done an excellent job with documenting the API and providing examples to use. There is an example to show functionality for almost 100% of the API, called API Demos. If you're familiar with Java and have already used Eclipse, getting your first app working should be fairly simple. If you've never coded anything in your life before, you will have a lot to absorb as you move forward, but don't get discouraged.

Get the SDK

The first step in getting started with the Android platform is to get the Android SDK (Software Development Kit). The SDK has the core libraries, an emulator, tools and sample code. I highly recommend using Eclipse and the android eclipse plugin. Eclipse IDE for Java Developers is fine if you are just doing Android. If this is your first Java development project, you will want to download the full Java SE Development Kit (JDK) as it contains tools you will need for signing and deploying your application.

Learn the application architecture

As tempting as it may seem to just dive right in, it's very important to understand the android application architecture. If you don't learn it, you may design things in such a way that will make it very difficult to fix problems with your game down the line. You will want to understand Applications, Activities, Intents and how they are all related to each other. Google has provided good information on the architecture here. The really important thing is to understand why your game may need to consist of more than one Activity and what that means to designing a game with good user experience. This is where things tie in to the Activity lifecycle.

Learn the activity lifecycle

The activity lifecycle is managed by the Android OS. Your activity will be created, resumed, paused and destroyed as the OS dictates. Handling these events correctly is very important to having an application that behaves well and does what the user perceives as correct. It's very good to know how all of this works before you start designing your game because you will save yourself debugging time and costly redesign time later on. For most applications, the default settings will work but for games, you may want to consider turning the SingleInstance flag on. When set as default, android will create new instances of the activity as it sees fit. For a game, you may only want to have 1 instance of the game activity. This has some implications for how you need to manage the state of things but for me it solved some resource management issues and it should be considered.

The main loop

Depending on what type of game you are writing, you may or may not have a main loop. If your game is not time-dependent or if it only responds to what the user does and will wait forever for user input without making any kind of visual changes, you may not need a main loop. If you are writing an action game or a game that has animations, timers or any kind of automation, you should seriously consider using a main loop.

The main loop of a game is the part that "ticks" sub systems in a specific order and usually as many times per second as possible. Your main loop will need to run on its own thread. The reason for this is that Android has a main UI thread and if you don't run your own thread, the UI thread will be blocked by your game which will cause the Android OS to not be able to handle any of its normal update tasks. The order of execution is usually as follows: State, Input, AI, Physics, Animation, Sound and Video.

Updating State means to manage state transitions, such as a game over, character select or next level. Often times you will want to wait a few seconds on a state and the State management is the part that should handle this delay and setting the next state after the time has passed.

Input is any key, scroll or touch from the user. It's important to handle this before processing Physics because often times input will affect the physics so processing input first will make the game more responsive. In Android, the input events come in from the main UI thread and so you must code to buffer the input so that your main loop can pick it up when the time comes. This is not a difficult task. Defining a field for the next user input and having the onKeyPressed or onTouchEvent set the next user action into that field is all that will be required. All the Input update needs to do at that point is determine if it is valid input given the state of the game and let the Physics side handle responding to it.

The AI update is analagous to a user deciding what they are going to "press" next. Learning how to write AI is out of the scope of this article but the general idea is that the AI will press buttons just like the user does. This will also be picked up and responded to by the Physics update.

The Physics update may or may not be actual physics. For action games, the point of it is to take into account the last time it was updated, the current time it is being updated at, the user input and the AI input and determine where everything needs to be and whether any collisions have occured. For a game where you visually grab pieces and slide them around, it will be the part that is sliding the piece or letting it drop into place. For a trivia game, it would be the part deciding if the answer is right or wrong. You may name yours something else, but every game has a part that is the red meat of the game engine and for this article, I'm referring to it as Physics.

Animations aren't as simple as just putting an animated gif into your game. You will need to have the game draw each frame at the right time. It's not as difficult as it sounds. Keeping state fields like isDancing, danceFrame and lastDanceFrameTime allows for the Animation update to determine if its time to switch to the next frame. That's all the animation update really does. Actually displaying the change of animation is handled by the video update.

The Sound update handles triggering sounds, stopping sounds, changing volumes and changing the pitch of sounds. Normally when writing a game, the sound update would actually produce a stream of bytes to be delivered to the sound buffer but Android manages its own sounds so your options for games are to use SoundPool or MediaPlayer. They are both a little sensitive but know that because of some low level implementation details, small, low bitrate OGGs will yield the best performance results and the best stability.

The Video update takes into account the state of the game, the positions of players, scores, statuses, etc and draws everything to screen. If using a main loop, you will want to use the SurfaceView and do a "push" draw. With other views, the view itself will call the draw operation and the main loop won't have to do it. SurfaceView gives the highest frames per second and is the most appropriate for games with animation or moving parts on screen. All the video update should do is take the state of the game and draw it for this instance in time. Any other automation is better handled by a different update task.

What's this code look like? Here's an example.

public void run() {
while (isRunning) {
while (isPaused && isRunning) {
sleep(100);
}
update();
}
}

private void update() {
updateState();
updateInput();
updateAI();
updatePhysics();
updateAnimations();
updateSound();
updateVideo();
}


3D or 2D?

Before you start on your game, you need to decide if you're going to go 3D or 2D. 2D games have a much lower learning curve and generally are easier to get good performance on. 3D games require much more in-depth math skills and may have performance issues if you are not very careful. They also require the ability to use modeling tools like 3D Studio and Maya if you intend to have shapes more complex than Boxes and Circles. Android supports OpenGL for 3D programming and there are many good tutorials on OpenGL that one can find to learn it.

Build simple, high quality methods

When getting started, make sure that you avoid writing one big long monolithic method that is "the game." If you follow the main loop pattern that I described above, this should be fairly easy. Each method you write should accomplish one very specific task and it should do so error-free. For example, if you need to shuffle a deck of cards, you should have a method called "shuffleCards" and that should be all it does.

This is a coding practice that applies to all software development but it's particularly important in game development. Debugging can get very difficult in a stateful, real-time system. Keep your methods small and the general rule of thumb is that each method should have 1 and only 1 purpose. If you're going to programatically draw a background for a scene, you may want a method called "drawBackground." Things like that will make it so that you develop your game in terms of building blocks and you will continue to be able to add what you need without making it too complex to understand.

It's all about efficiency!

Performance is a major issue for any game. The goal is to make the game as responsive as possible and to also look as smooth as possible. Certain methods like Canvas.drawLine are going to be slow. Also drawing an entire screen-sized bitmap onto the main canvas every frame will also be costly. Balancing things like that is necessary to achieve the best performance. Make sure to manage your resources well and use tricks to use the least amount of CPU to achieve your task. Even the best game will not be very fun if it can't perform well. People in general have little tolerance for choppiness or poor response.

Tips and Tricks

Take a look at the example for LunarLander in the SDK. It uses a SurfaceView and that would be the appropriate view to use for a game that needs the highest number of frames per second possible. If you're going 3D, there is a GLView in the examples that handles a lot of initialization required to get a 3D display. For LightRacer, I had to optimize the way I have everything drawn or else the framerate would be drastically lower. I drew the background to a Bitmap only once which was when the view is initialized. The light trails are in their own bitmap which gets updated as the racers move. Those two bitmaps are drawn to the main canvas every frame with the racers drawn on top and then finally an explosion. This technique made the game run at a playable rate.

It's also a good practice to have your bitmaps be the exact size you intend to draw them on screen, if applicable. This makes it so that no scaling is needed and will save some CPU.

Use a consistent Bitmap Configuration (like RGBA8888) throughout the game. This will save the graphics library CPU in having to translate the different formats.

If you're determined to develop a 3D game but have no 3D knowledge, you will want to pick up a book or two on 3D game programming and study up on linear algebra. At a bare minimum, you must understand dot products, cross products, vectors, unit vectors, normals, matrixes and translation. The best book I have come across for this math is called Mathematics for 3D Game Programming and Computer Graphics.

Keep the sound small and at a low bitrate. The less there is to load, the faster loading times will be and the less memory the game will use.

Use OGGs for sound, PNGs for graphics.

Make sure to release all media players and null out all of your resources when the activity is destroyed. This will ensure that the garbage collector gets to everything and that you don't have any memory leaks between launches of the game.

Join the Android Google group and find community support. There will be people that can help you along the way.

Above all, spend time testing and retesting and making sure that every little thing works exactly the way you would expect it to. Polishing the game up is the longest and hardest part of development. If you rush it out to market, you will probably have a disappointed crowd and you may feel that all your hard work is wasted. It's not possible to have 100% of people love what you write but you should at least try to put out the highest quality work that you can.

Google has excellent documentation for getting started here.