Archive

Posts Tagged ‘Objective C’

OpenSource OpenGL 2D RPG Engine (iPhone)

April 10, 2012 8 comments

Hey guys,

if you are following this blog long enough than you surely know that I was writing a 2D RPG Engine about 9 months ago. Well, i stopped the project at the end of last year due to more interesting stuff but had a lot of questions in the last time if I couldn’t make it available for the community.

So here we go.. Download the Engine Source code here:

https://github.com/MarkusPfundstein/OpenGL-2D-RPG-Engine-iPhone

READ THIS: – The code is by NO MEANS perfect. It was the first time that I approached a big project like this, so stuff is sometimes a bit messy. Especially the implementation of the script engine is nothing i am proud of but ok 🙂 I think for learning purposes it is pretty good. The engine can render up to 3000×3000 tiles on 60 fps on a ipod 4th gen.. That is quite powerful 🙂

Okidok! Have fun..

P.S. The source code for the Level Editor written in Cocoa and C++ will be online next week! Stay tuned 🙂

Cocos2dx Extensions: CCValue

March 25, 2012 1 comment

Hey guys,

i added another small extensions to my github. It is a template class which can be used like the NSValue class from the Cocoa library so basically speaking it allows you to store structs like CCRect/CCPoint in CCArray and CCDictionary which can be incredible useful sometimes.

Clone it here: https://github.com/MarkusPfundstein/Cocos2DX-Extensions

Some example code:


CCRect t1;

t1.size = CCSizeMake(100, 200);

t1.origin = CCPointMake(10, 33);


CCValue<CCRect> *value = CCValue<CCRect>::valueWithValue(t1);


CCArray *array = CCArray::arrayWithCapacity(2);

array->addObject(value);


CCRect t2;

t2.size = CCSizeMake(100, 200);

t2.origin = CCPointMake(10, 33);


CCValue<CCRect> *v2 = CCValue<CCRect>::valueWithValue(t2);


CCValue<CCRect> *v3 = static_cast<CCValue<CCRect> *>(array->objectAtIndex(0));


CCValue<CCRect> *v4 = CCValue<CCRect>::valueWithValue(v3);

CCRect rectv4 = v4->getValue();

rectv4.size = CCSizeMake(10,10);


if (v3->isEqualToValue(v2))

{

    CCLog("Equal");

}


if (v3->isEqualToValue(v4))

{

    CCLog("still equal");

}

Cocos2dx Extension – Vertically and Horizontally flipped Tiles

March 18, 2012 2 comments

Hey guys,

for a new game i am currently working on, I decided to use the cocos2dx library in combination with the tiled level editor. After writing several 2d engines from scratch I decided that it is just too much work to get it really really right (especially with a this cool features, that modern games must have) and that I actually wanted to start making a game. The library its just awesome and was currently released as version 1.0.

One thing I found out yesterday that there was no support for flipped tiles when exporting a map from the Tiled Map Editor. I think that this was a big shame and digger into the source codes of both, Cocos2dx and Tiled to find out the right way to to implement it. You can get the final result here:

https://github.com/MarkusPfundstein/Cocos2DX-Extensions

It was actually pretty easy. Tiled Editor stores all maps in TXML format which is actually a dialect of the XML format. When saved uncompressed, you can see that maps are stored like this:

<layer name=”Tile Layer 1″ width=”5″ height=”5″>
<data>
<tile gid=”1″/>
<tile gid=”2147483649″/>
<tile gid=”0″/>
<tile gid=”2147483650″/>
<tile gid=”2″/>
<tile gid=”1073741825″/>
<tile gid=”3221225473″/>
<tile gid=”0″/>

In this example, the tile vids with 1 and 2 are the original tiles 1 and 2 unflipped. This high integer values are the tile values for flipped tiles.

I found out, that the guy who implemented flipping in the Tiled Editor uses two bit flags to check wether a tile is flipped or not.

Horizontally Flipped = 0x80000000

Vertically Flipped = 0x400000000

So in order to determine if a tile is flipped we just have to check against these two flags and than clear the gid. Pretty easy 🙂

To use it, download the two source files and replace them with those in you current cocos2dx library. It will work out of the box (supposed that you don’t have made any changes on your own) 🙂

The only affected cocos2dx method is CCTXMLLayer::appendTileForGid( … )

It is a very basic implementation until now and supports the flipping of tiles at the parsing stage. If you want to change or add tiles at runtime you are on your own 🙂 But I believe most of us won’t do it.

Have fun!

Markus

Hint 1: Using flipped tiles reduces the size of your tile set dramatically and allows for more proper level design.

Hint 2: Press ‘x’ or ‘y’ to flip on the respective axles.

Pictures of my new game:

Adding Swiping to UIWebView using JavaScript (iOS4/iOS5) and custom URL scheme

December 25, 2011 2 comments

Hey folks, welcome to my new Tutorial, which will cover the topic of how to implement swiping in a UIWebView using JavaScript and will explain how to send variables back to our UIWebView using Custom URL Schemes.

The code itself is straightforward but will cover a lot of cool things.

To load the JavaScript code we call in our webViewHasFinishedLoading Delegate method the following code:


#define DEVICE_IOS5()   (([[[UIDevice currentDevice] systemVersion] floatValue]>=5.0))

if (DEVICE_IOS5()){

     [self.webView stringByEvaluatingJavaScriptFromString:@"loadListenerIOS5()"];

} else {

     [self.webView stringByEvaluatingJavaScriptFromString:@"loadListenerIOS4()"];

}

This is the JavaScript code:

File: SwipeEvent.js

Use your local html to let the browser know about it. I showed how to do that in my last tutorial.

         EDIT: One more thing. When copying JavaScript files from the Finder to XCode, it will not treat it as an added resource but as a file to compile. Of course that is not possible so it will give you warnings back and the file will not be copied to the resource folder on your device / simulator. To fix that problem, go to target->build settings and than drag and drop the file from Compile Sources to Copy Bundle Resources.


var startX;

var startY;

var diffX;

var diffY;

var hypo;

var isIOS5;

function loadListenerIOS4(){

    var body = document.getElementsByTagName("body")[0];

    body.addEventListener("touchstart", touchStart, false);

    body.addEventListener("touchend", touchEnd, false);

    body.addEventListener("touchmove", touchMove, false);

    isIOS5=0;

    return "JS Listener iOS4 loaded";

}

function loadListenerIOS5(){

    var body = document.getElementsByTagName("body")[0];

    body.addEventListener("touchstart", touchStart, false);

    body.addEventListener("touchend", touchEnd, false);

    body.addEventListener("touchmove", touchMove, false);

    isIOS5=1;

    return "JS Listener iOS5 loaded";

}

function fireSwipeScheme(a,b,c){

    var scheme="swipe:" + a + ":" + b + ":" + c;

    document.location=scheme;

}

function touchStart(event){

    diffX=0;

    diffY=0;

    if (isIOS5){

        startX=event.pageX;

        startY=event.pageY;

    } else {

        startX=event.touches[0].pageX;

        startY=event.touches[0].pageY;

    }

}

function touchEnd(event){

    if (diffX>50||diffX<-50){

        fireSwipeScheme(diffX,diffY,hypo);

    }

}

function touchMove(event) {

    var a;

    var b;

    if (isIOS5){

        a = event.pageX - startX;

        b = event.pageY - startY;

    } else {

        a = event.touches[0].pageX - startX;

        b = event.touches[0].pageY - startY;

    }

    var c = calcHypotenuse(a,b);

    diffX=a;

    diffY=b;

    hypo=c;

}

function calcHypotenuse(a,b){

    return Math.sqrt(a*a,b*b);

}

LoadListenerIOS4 and LoadListeneriOS5 are there to attach an event listener to the body object of our DOM. The first is a constant which can be either “touchstart”, “touchend”, “touchmove” or “touchcancel” and the second determines the function we are about to call. So in this case, “touchstart” calls touchStart( … ).

Both loaders are the same, only that one sets the variable isIOS5 to 1 and the other sets it to 0. This is important if you look into the touchStart function.

On iOS5, to get the current x/y coordinates of a touch, some must call event.pageX and event.pageY, while on iOS4, its event.touches[0].pageX and event.touches[0].pageY.

The same goes for all the other touch function.

So what we do here is, we register the touch coordinates on touch start, calculate all the way through touchmove the current distance and fire immediately an url scheme to the UIWebView if the touches ended.

fireSwipeScheme is also pretty interesting.

The variable “scheme” is a combination about the keyword “swipe” and a, b, c which are actually the variables diff x, diff y and the hypotenuse.

So, how do we now get these variables into our objective C Code?

In the delegate method webViewShouldStartLoadOnRequest: withNavigationType:  we add the following code

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{

    NSString*scheme=[[request URL]absoluteString];

    //NSLog(@"received scheme: %@", scheme);

    NSArray *components=[scheme componentsSeparatedByString:@":"];

    if ([[components objectAtIndex:0]isEqualToString:@"swipe"]){
        [self handleSwipeInWebView:components];
        return NO;
     }

     return YES;

}

And to our class we add the following method:


#define kMinSwipeDistanceHorizontalInWebView 100.0f

#define kMaxSwipeDistanceVerticalInWebView 50.0f

-(void)handleSwipeInWebView: (NSArray*)code{

    @autoreleasepool {

        NSInteger swipeDistX=[[NSNumber numberFromString:]intValue];

        NSInteger swipeDistY=abs([[NSNumber numberFromString:]intValue]);

        NSLog(@"swipeDistY: %d", swipeDistY);

        NSLog(@"swipeDistX: %d", swipeDistX);

        if ((swipeDistX>kMinSwipeDistanceHorizontalInWebView||swipeDistX<-kMinSwipeDistanceHorizontalInWebView)&&swipeDistY>kMaxSwipeDistanceVerticalInWebView){

            MasterViewController*ptr=(MasterViewController*)self.masterViewController;

            if (ptr){

                 if (swipeDistX<-kMinSwipeDistanceHorizontalInWebView){

                     [ptr moveDownInTreeHierarchy];

                 } else if (swipeDistX&gt;kMinSwipeDistanceHorizontalInWebView){

                     [ptr moveUpInTreeHierarchy];

                 }

           }

     }

}

In the delegate method, the UIWebView catches all clicked links events or all document.location events from JavaScript. By sending a custom URL script we can first return NO, to make sure that no new page will get loaded and we can use it actually as an encoder for our variables. The url scheme in this example looks something like this

 

Swipe:-40:30:-50.

In the delegate method, I break the array into components by separating them by the ASCII character ‘:’. In the handleSwipeInWebView method, I can easily use the components than to let my code do what I want him to do.

In my case, I am firing the swipe Event from the DetailViewController in my SplitViewController and notify the MasterViewController about it.

 

UIWebView – Calling Javascript from iOS with passing parameters … Tutorial

Right now I am working on a project for my company which has to display a lot of content, that is delivered in the XML format and consists of small chunks of HTML formatted text-code.

The only way on iOS to display HTML is to use the UIWebView and so I had to write some small Javascript code which can be called to display the content. It has to be dynamic, as the content is sorted by chapters and the user can navigate through a tree structure to display only relevant content.

So what I did was, parsing the XML and than writing everything into Core Data (btw. Core Data is AWSOME!… the speed, the usability.. incredible). With Core Data I do the sorting and when the user opens content, I load my local HTML, which does actually nothing but tell the browser where the .JS files are.

The reason why I write this tutorial is because I couldn’t find a good one on the web that covers that topic which my requirements (.JS files, parameter passing from Objective C to JS)

Btw: this is not finished code , it works but is rough to get the basic system to work.

<html>

<head>

<meta http-equiv=“content-type”content=“text/html;charset=utf-8” />

<title>DisplayContent</title>

<style>

</– th, td { vertical-align: top; } –>

</style>

<script src=“displayContent.js”></script>

</head>

<body>

</body>

</html>

The JS File looks like this:

function textNodeWithColorAndSize(text, rgb, size, font){

    var textNode=document.createTextNode(text);

    var font=document.createElement(“font”);

    font.style.color=rgb;

    font.style.fontSize=size;

    font.appendChild(textNode);

    return font;

}

function printHeadOfTable(code, headline) {

    var body = document.getElementsByTagName(“body”)[0];

    var table     = document.createElement(“table”);

    var tableBody = document.createElement(“tbody”);

    var row = document.createElement(“tr”);

    for (var i = 0; i < 3; i++) {

        var cell = document.createElement(“td”);

        var cellText;

        if (i==0){

            cell.width=40;

            cellText=textNodeWithColorAndSize(code, “#FF3344”, “12px”, “font”);

        } else if (i==1) {

            cellText=textNodeWithColorAndSize(headline, “#000000”, “18px”, “font”);

        } else {

            cellText=textNodeWithColorAndSize(“[E-Mail Button]”, “#555555”, “12px”, “font”);

        }

        cell.appendChild(cellText);

        row.appendChild(cell);

    }

    tableBody.appendChild(row);

    table.appendChild(tableBody);

    table.style.marginLeft=20;

    body.appendChild(table);

}

function resize(){

    var frames=document.getElementsByTagName(“div”);

    for (var i=0;i<frames.length;i++){

        frames[i].style.width=“94%”;

    }

}

function printBodyOfTable(bodyString){

    var body = document.getElementsByTagName(“body”)[0];

    var frame=document.createElement(“div”);

    frame.innerHTML=bodyString;

    frame.style.width=“94%”;

    frame.style.marginLeft=20;

    body.appendChild(frame);

}

So, fine enough. Resize does resize all elements <div> to 94% of the web view browser. This is important because of rotation.

printBodyToTable( … ) and printHeadToTable ( … ) are functions to print content.

So, when you load up now the webpage nothing appears of course. Thats why we have to call the JS from our Objective C code. This looks like this:

The array assets is passed from the fetchedResults.

-(void)loadWebViewWithAssets: (NSArray **const)assets{

    [self setWebViewFrame:CURRENT_ORIENTATION()];

    @autoreleasepool {

        _assetArray=[[NSArray alloc]initWithArray:*assets];

        NSURL *file = [NSURLfileURLWithPath:[[NSBundle mainBundle]pathForResource:@”contentView”ofType:@”html”]];

        NSURLRequest *urlRequest=[[NSURLRequest alloc]initWithURL:file];

        [self.webView loadRequest:urlRequest];

        [urlRequest release];

    }

}

So this loads our basic html. Now when the webView is done loading, we are free to call our Javascript functions.

I think it is a bit a pity, that the JS Script methods ONLY work AFTER the web view finished its loading. It would be cool to do that beforehand but well.. lives a b**** sometimes 🙂

Code:

(Asset and Content are Core Data entities.)

The abort calls are there for debugging purposes.

-(void)webViewDidFinishLoad:(UIWebView *)webView{

     for (int i=0;i<[_assetArray count];i++){

         Asset*asset=[_assetArray objectAtIndex:i];

         Content*content=asset.content;

         if (!content){

             dbgAbort();

         }

         NSString *jsPrintHeadOfTable=[[NSString alloc]initWithFormat:@”printHeadOfTable(\”%@\”, \”%@\”)”, asset.at_code, asset.at_title];

         NSString *jsPrintBodyOfTable=[[NSString alloc]initWithFormat:@”printBodyOfTable((\”%@\”))”, content.at_content];

         if (!jsPrintBodyOfTable){

             dbgAbort();

         }

         [self.webView stringByEvaluatingJavaScriptFromString:jsPrintHeadOfTable];

         [self.webView stringByEvaluatingJavaScriptFromString:jsPrintBodyOfTable];

         [jsPrintHeadOfTable release];

         [jsPrintBodyOfTable release];

     }

     [_assetArray release];

}

So, you see, we are even able to pass parameters to our Javascript which is incredible awesome. The same webView method is able as well to retrieve values from the javascript by capturing the NSString return value. But there a lot of tutorials out there for this.

Ah yeah, and is very important that you prepare your parameters. content.at_content for example is a complete HTML formatted text. Before you pass it you have to make sure that there are no _ ” _ characters in it (replace them with _ ‘ _  . Now newline characters, no carriage return etc.. Just to tell you 🙂 Took me some while to get the idea. 😉

From this point on we can now call the stringByEvaluatingJavaScriptFromString method wherever and whenever we like.. Cool huh?

Yeah, this was my first tutorial and I hope it helped ..

Greetings

Markus