Cocos2d-x Cross Platform Video Player – Part 2 Android

In the last post, we saw how to create a skeleton to handle multiple platforms with the `WrapperClass` and how to implement video player in iOS. Please go through the first post, to get a good understanding of this post.

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-android/archive/master.zip

Complete Project can be found in GITHUB.

1. Project Creation

For setting up the project, refer this page. Create a project named `VideoPlayer` and come back here once you got the `Hello World` running in Android.

2. Copy Wrapper Classes from previous Tutorial

Copy the WrapperClass(.h, .cpp), AudioManager(.h, .cpp), Config(.h, .cpp), AppDelegate(.h, .cpp) and HelloWorldScene(.h, .cpp) files to the newly created Android project. If all configurations are done right and the old CPP files are copied in right way, running the project should show `Click to play Video` in the center of the screen. But one catch, it will show a Compilation error, since the `CCApplication` class wont be having `playVideo()` function, which is called from the `WrapperClass`. This line can be commented out temporarily to check if everything is configured right and if the label `Click to play Video` is being rendered.
Cocos2dx Cross Platform Video Player Screenshot

3. Passing control from C++ to Java

We already have the touch handler in HelloWorldScene, which calls on `WrapperClass->playVideo()), when a touch is made.

Branching to Android specific code lies in `WrapperClass`, which calls `playVideo()` function of `CCApplication`. So lets add that function in `CCApplication`(.h, .cpp). The corresponding file will be located @ $COCOS2DX_HOME/cocos2dx/platform/android/CCApplication.h/cpp, where $COCOS2DX_HOME is the path where you installed coos2d-x files.

Add the following code to that class:

void CCApplication::playVideo(){
    JniMethodInfo minfo;
    if(JniHelper::getStaticMethodInfo(minfo,
                                      "in/gethugames/VideoPlayer",
                                      "playVideo",
                                      "(Ljava/lang/String;)V")) {
        jstring StringArg1 = minfo.env->NewStringUTF("sample.mov");
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID, StringArg1);
        minfo.env->DeleteLocalRef(StringArg1);
        minfo.env->DeleteLocalRef(minfo.classID);
    }
}

The `getStaticMethodInfo` function of `JniHelper` defines the static JAVA function, which needs to be invoked. 2nd parameters defines a static Java function named playVideo, with first parameter stating the class path in.gethugames.VideoPlayer. And the static JAVA function is expected to take a string parameter, which is defined by the cryptic third parameter. The function `CallStaticVoidMethod` makes the actual function call to JAVA function.

For now, just add an empty function in the JAVA activity class `VideoPlayer` (.java)

public static void playVideo(final String msg) {
    System.out.println("Play Video in JAVA class in invoked");
}

Build and tun the project now and if everything is done right, when the `MenuItemLabel` is touched, the above line should get logged in LogCat. Wow, we passed the control from CPP to JAVA after much effort. Now is the time to play the video.

4. Invoking a separate Video Activity

I am not an android guy and there may be better solutions to play video than what I write here. Please comment, if any other easy / better methods are there.

Add the following code to the VideoPlayer class:

// in VideoPlayer class
public static VideoPlayer selfActivity = null;

// inside onCreate()
selfActivity = this;

// in playVideo() function
Intent i = new Intent();
i.setClass(selfActivity, Cocos2dxVideo.class);
selfActivity.startActivity(i);

`selfActivity is a object reference to the the VideoPlayer object. It is assigned in onCreate() and in `playVideo()`, an Intent is created to switch to another activity, named `Cocos2dxVideo`. This new activity will play a video and when touched in middle, will close itself and also notifies our CPP part of the project.

5. Cocos2dxVideo

Create a layout file `activity_cocos2dx_video.xml` in the res/layout folder with the following contents:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="2dp"
android:paddingLeft="2dp"
android:paddingRight="2dp"
android:paddingTop="2dp"
tools:context=".Cocos2dxVideo" >

    <VideoView
    android:id="@+id/videoPlayer"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true" />

</RelativeLayout>

Above code defines a standard VideoView control that is used to play video in Android. Then add the following code in `Cocos2dxVideo` (.java) class, which gets a reference to the VideoView defined in the layout above, attach the video in `res/raw` and then plays it. Now register the Activity in `AndroidManifest.xml` as follows:

<activity android:name="in.gethugames.Cocos2dxVideo"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
</activity>

Two points to note here:

1. I didn’t passed the video path from the main activity (VideoPlayer) to here, but instead directly plays the video in `res/raw` folder. (will try and update post, when I get time)

2. The video shouldn’t be in the standard resources folder where sprites and other assets are copied. That folder is for cocos2d-x, but since we play the video from JAVA land, the video should be in `res/raw` folder and its suggested that only lowercase should be used in naming the video file.

        // in Cocos2dxVideo class
        private Cocos2dxVideo me;
        public native int onOver();

        // inside onCreate function of Cocos2dxVideo class
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cocos2dx_video);
        me = this;
        VideoView vv = (VideoView) findViewById(R.id.videoPlayer);
        vv.setMediaController(null);
        vv.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.samplevideo));
        vv.start();
        vv.requestFocus();

If the project is run, clicking on the menu item label should invoke the video. Happy? wait, its not over yet. The activity should be closed either after video playback, or if user touches the screen. Add the following event listeners to do the same:

        // invoked when the video playback is complete
        vv.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
            	me.onOver();
                me.finish();
            };
        });

        // invoked when the video view is touched
        vv.setOnTouchListener(new View.OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
            	me.onOver();
            	me.finish();
                return false;
            }
        });

Both the listeners does the some thing, calls the native function `onOver() and end the activity by calling, `finish()`. This resumes the previous activity, which the our `VideoPlayer`. When the video started playing, the in-game music is paused. So, we need to resume it, when the video is done. That’s the reason for calling the onOver().

6. JAVA to CPP – Video Completion notification

The function onOver() is marked with `native` keyword to denote its real implementation is in CPP and control should be transferred. JAVA to CPP is very simple. Just name a function in CPP in the following format to achieve it:

return-type Java_[CLASSPATH]_[FUNCTION_NAME](JNIEnv *env, jobject thiz) { }

So, add the following code anywhere within a extern “C” {} block, I added in jni/hellocpp/main.cpp file.

        void Java_in_gethugames_Cocos2dxVideo_onOver(JNIEnv *env, jobject thiz) {
	CCLog("CPP Function called");
	AudioManager::sharedManager()->playBG();
}

Hence the loop is complete and thus the game sound track gets played after the video is over.

Thats the end of the Tutorial !!!

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-android/archive/master.zip

Complete Project can be found in GITHUB.

Cocos2d-x Cross Platform Video Player – Part 1 iOS

Cross Platform (iOS and Android) Video playing solution is very much possible in cocos2d-x 2.1.5. But as far as I searched there is not single tutorial / forum discussion that explains it with ease. So here I am summarising how I achieved it. There is no Video Player control in cocos2d-x. So we need to fall back to native video players of iOS and Android platforms. Android Video Playback in 2nd part of this tutorial.

Objective:

To write a single function that plays a video in either iOS / Android, given its file name. Also, the video should be made skippable, by tapping on it. The game’s sound track should be muted when the video gets played, since it might have its own soundtrack and the game’s sound track will be resumed, when the video is stopped.

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-ios/archive/master.zip Complete Project can be found in GITHUB.

1. Generic Wrapper Class

To handle Features, which require separate implementations on iOS and Android, I implemented wrapper class(WrapperClass.h, WrapperClass.cpp), which will be called from the game code. Then there will be separate Platform dependent wrapper classes, which will get called from WrapperClass. The only functionality of WrapperClass is to forward the flow to platform dependent functions. Here is the code of the Wrapper Class:

//WrapperClass.h
class WrapperClass : public cocos2d::CCObject {
public:
    void                            playVideo(const char *vidPath);
    static WrapperClass             *getShared();
};

//WrapperClass.cpp
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#include "misc/iOSWrapper.h"
#endif

static WrapperClass *instance   =   NULL;

void WrapperClass::playVideo(const char *vidPath) {
    AudioManager::sharedManager()->stopBG();
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
    iOSWrapper::getShared()->playVideo(vidPath);
#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
    CCApplication::sharedApplication()->playVideo(vidPath);
#endif
}

WrapperClass *WrapperClass::getShared() {
    if (!instance) {
        instance                =   new WrapperClass();
    }
    return                          instance;
}

The `playVideo` function just calls platform specific function from here. Since playback in Android requires JNI calls to pass the flow to JAVA side, I implemented the required function in CCApplication, which already has few JNI calling functions. Also, this function stops the background music, since the Video will be having its own Audio Track. Game’s sound track will be resumed when the video gets over.

2. Playing Video in iOS

The `iOSWrapper` singleton (iOSWrapper.h, iOSWrapper.mm) called in above code is another wrapper that bridges the CPP side of cocos2d-x and Objective-C part of native `UIView` code. The `playVideo` function of `iOSWrapper` class is as follows:

void iOSWrapper::playVideo(const char *vidPath) {
    NSString                        *string, *stringPath;
    stringPath                  =   [NSString stringWithUTF8String:vidPath];
    string                      =   [[NSBundle mainBundle] pathForResource:stringPath ofType:@"mp4"];
    [[EAGLView sharedEGLView] playTutorialVideo:string];
}

The above code converts the date type of file name from `char *` to `NSString` and then find its path in the Bundle and calls another custom function `playTutorialVideo()` written in `EAGLView`. That function is responsible for actual video play. Add the following in `EAGLView` to complete the iOS part.

 (void) playTutorialVideo:(NSString *)path {

    NSURL                           *url;

    url                         =   [NSURL fileURLWithPath:path];
    player                      =   [[MPMoviePlayerController alloc] initWithContentURL:url];
    player.view.frame           =   CGRectMake(0, 0, self.frame.size.height, self.frame.size.width);
    player.fullscreen           =   YES;
    player.scalingMode          =   MPMovieScalingModeNone;
    player.controlStyle         =   MPMovieControlStyleNone;
    [self                           addSubview:player.view];
    [player                         play];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(removeVideo)
                                                 name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
}

Now we are completely into iPhone Native part, where we use Objective-C and deal with UIView and UIViewControllers, completely away from cocos2d-x land. The above function creates a `MPMoviePlayerController`,  loads the video with given path and plays it. MPMoviePlayerController throws notifications instead of calling delegate callback functions, in case of internal events. So the above code also registers with NSNotificationCenter to capture video pause / stop events.

(void) removeVideo {
    if (player.playbackState == MPMoviePlaybackStatePaused || player.playbackState ==   MPMoviePlaybackStateStopped) {
        [player.view removeFromSuperview];
        [player release];
        player = nil;

        [[NSNotificationCenter defaultCenter] removeObserver:self
        name:MPMoviePlayerPlaybackStateDidChangeNotification
        object:nil];

        AudioManager::sharedManager()->playBG();
    }
}

The above function is registered to handle notifications of MPMoviePlayerController and will get invoked on any change in state. We need to remove the controller, only when the video playback is over and so we check for the Stopped/Paused state and when they happen, we remove the controls, release from memory, resumes the game background music.

Skip on Touch

Not all players will sit through the videos / instructions, but they want to directly get hooked up into the gameplay. So, we will make the video stop and disappear, when user taps on it. To do it, just add the following block of code inside `touchesBegan` function of EAGLView, immediately after `if (isKeyboardShown_)` block:

if (player) {

    [player stop];
    [player.view removeFromSuperview];
    [player release];
    player = nil;

    [[NSNotificationCenter defaultCenter] removeObserver:self
    name:MPMoviePlayerPlaybackStateDidChangeNotification
    object:nil];

    AudioManager::sharedManager()->playBG();

    return;
}

To keep the posts short, I will write about Android part of the Video Player in next post. Once all this is done, setup a Layer (HelloWorldScene.hHelloWorldScene.cpp) with a menu button, whose handler has the following one line of code:

WrapperClass::getShared()->playVideo("sampleVideo");

If everything is done right, you will have a button in screen, which when clicked, stops the game sound track, and plays the video. Once the video is over or skipped, the game sound track resumes.

Download

Download project @ https://github.com/saiy2k/cocos2dx-videoplayer-ios/archive/master.zip Complete Project can be found in GITHUB.

Android

Android Video playback is explained in the second part of this tutorial.

C Program for 21 Sticks game

Here is a good brain refreshing puzzle that I solved recently. Thanks to my friend Raghul for sharing this good piece of puzzle with me. Its a turn based 2 player game (player and computer in our case). c

Now, I am to write a program for this game in such a way that however the player plays, he always picks the last one and thus the computer wins. Looked very simple at first and I came with the following program in 15 minutes and thought to myself (very proudly) that I solved it.

    
    int lineCount;
    int userPick;
    int aiPick;

    lineCount = 21;
    while (lineCount > 1) {
        printf("\n\n%d lines remaining. Pick 1 or 2 or 3 or 4: ", lineCount);
        scanf("%d", &userPick);
        lineCount -= userPick;

        if (lineCount == 1) {
            printf("\nyou win and AI lose\n");
            return;
        }
        if (lineCount <= 5) {
            aiPick = lineCount - 1;
        } else {
            aiPick = rand() % 4 + 1;
        }

        printf("you took %d lines", userPick);
        printf("\nAI took %d lines", aiPick);
        lineCount -= aiPick;
    }
    if (lineCount == 1) {
        printf("\nyou win and AI lose\n");
        return;
    } else {
        printf("\n1 line remains and u lose\n\n");
    }

The AI seems to be working good, but its not perfect. It fails occasionally, especially when the player gets a turn with the remaining number of sticks in the range 2 to 5. So I tried to fix this by playing in such a way that AI always gets its turn when the remaining number of sticks is in the range 2 to 5. But I couldn’t figure out how to do this inside my little mind. So I took a pen and paper and started writing the remaining number of lines, all possibilities of AI’s move and corresponding result, which resulted in a table such as this:

Possible States with results
Remaining Sticks AI Move Worst Case
2 1 Win
3 2 Win
4 3 Win
5 4 Win
6 1 Lose
2 Lose
3 Lose
4 Lose
7 1 Win
2 Lose
3 Lose
4 Lose
8 1 Lose
2 Win
3 Lose
4 Lose
9 1 Lose
2 Lose
3 Win
4 Lose
20 1 Lose
2 Lose
3 Lose
4 Win
21 1 Lose
2 Lose
3 Lose
4 Lose

Only after completely writing out the above table, I found that when there are some certain magic remaining sticks at your turn, regardless of your move, if ur opponent plays his best move, he can win. The magic numbers are found to be 21, 16, 11, 6 and of course 1.

Since the player begins the game with 21 remaining sticks (one of the magic number), as long as my AI plays its best move, it can win. The AI’s best move nothing but the move which makes the remaining number of lines to the next lowest magic number and as we continue when the player plays his move, he will have the remaining sticks as 16, 11, 6 and finally 1 and he loses. Here is the final working program with perfect AI:

#include <stdio.h>

int lineCount;

void main() {

    int userPick;
    int aiPick;

    lineCount = 21;
    while (lineCount > 1) {
        printf("\n\n%d lines remaining. Pick 1 or 2 or 3 or 4: ", lineCount);
        scanf("%d", &userPick);

        lineCount -= userPick;

        if (lineCount >= 17 && lineCount <= 20) {
            aiPick = lineCount - 16;
        } else if (lineCount >= 12 && lineCount <= 15) {
            aiPick = lineCount - 11;
        } else if (lineCount >= 7 && lineCount <= 10) {
            aiPick = lineCount - 6;
        } else {
            aiPick = lineCount - 1;
        }

        printf("you took %d lines", userPick);
        printf("\nAI took %d lines", aiPick);
        lineCount -= aiPick;
    }

    printf("\n1 line remains and u lose\n\n");
}

I am sure this is not the best way to solve this problem, I should have done some research on maths and find some elegant solution, but I guess this is fine for the first step and to achieve the goal. I will look into other possible elegant solutions and if I find any, I will post about it later.

Happy Programming !!!

Tic Tac Toe Extended

I started this project nearly 9 months ago planned to finish in a month, but because of my laziness dragged till now and finally I completed the development now. Very big thanks to my friend Sridhar for his awesome idea of extended Tic Tac Toe game. At first I never thought it would be much fun, but I started doing it because it seemed simple from development point of view. But when I play the game now with any of my friend, I feel the game addictive.

Game is hosted at http://www.gethugames.in/tictactoe/

 

Source code available at https://github.com/saiy2k/Tic-Tac-Toe-Extended-HTML5-Game

 

Game Description:

This is an extended version of Tic Tac Toe game with a board size of 9×9. The player scores a point on placing his sign in 4 consecutive tiles. The game can be played with another human player or with AI. At the end of the game based on player’s score and timing a Badge is awarded which can be shared to Facebook / Twitter. This game also supports HTML5 offline mode, thus requiring no internet connection to play after first run.

Tic Tac Toe Extended – Screenshot

HTML5 Features:

* Uses Canvas Tag to draw the game board and player marks.
* supports Offline mode
* uses CSS media queries to scale to different screen sizes.
* Playable on all devices from Desktop, Tablet to mobile devices (with minimum resolution of 320×480)

Class Design:
* GameManager: Handles high level game flow and handles UI controls and their events(using jQuery).
* InputManager: manages game specific input.
* GameState: holds the state of the game from game tile array, scores, clicked tile, elapsed time, player names, etc.,
* GameLogic: core game logics like score calculation, checking for win/lose condition, AI.
* GameBoard: updates and draws the gameboard using an array of BoardTile objects.
* BoardTile: handles a single tile in the board, updates and draws them.
* FBWrapper: connects to facebook and shares the game status.
* TwitterWrapper: connects to twitter and shares the game status.

Game AI:

The challenging part of this project is the AI. At first, I tried implementing some tricky AI which will track all your moves and it either go full defense mode, in which it tries to block tiles making hard for the player to score or go in full attack mode, in which the AI only concentrated on getting score for itself. But implementing those logics proved hard for me.

So I fell back to brute force algorithm which scans the whole board in 8 directions(top to bottom and reverse, left to right and reverse, top left to bottom right and reverse, top right to bottom left and reverse). While scanning cell by cell, it adds up a temporary variable for same consecutive non blank tile it passes. That is if the first nonblank tile is the AI tile itself, and 4 such tile are placed adjancently, then it increases the score of 5th non blank tile by 4.

This same logic is repeated for all rows, columns and diagonals in both directions and a weight-age score is given for each and every cell in the process. In the end, the cell with high weight-age will be marking during the AI’s next move.

Scoring System:

The scoring system is very simple, there is a rank calculated based on which the player is given a badge. Higher rank is given as the difference between player’s score and opponent’s score is high and the rank value is lowered as the player spends more time than the opponent.

Offline Mode and Media Queries:

This is the first time for me to try with this technologies. They were very simple but very useful powerful technologies.

Overall, I learned a hell lot from this project including Audio API and many others mentioned above. From my next project, I well try to implement all the things that I learned. And I am thinking of supporting IE from my next project onwards. Hope IE wont be so stubborn in supporting modern web standards.