Sunday, December 2, 2012

libgdx and iOS deployment quest - Part IV

I discovered that my installation of TortoiseHg was not 100% complete.  I had not installed kdiff3 or added any of the necessary configurations to get that tool to work.  The net result was that I was not able to do any sort of merges or "diff to local" type operations.  After a bit of hunting, there are additional configurations that you need to implement after installing kdiff3.

To wit:

1) Install kdiff3 from kdiff3.sourceforge.net.
2) Update your .hgrc similar to below:


[extensions]
hgext.bookmarks =
hggit =
mq =
hgext.extdiff =
[extdiff]
cmd.kdiff3 =
[merge-tools]
kdiff3.args = $base $local $other -o $output
kdiff3.priority=-3
kdiff3.args=--auto --L1 base --L2 parent1 --L3 parent2 $base $local $other -o $output
;kdiff3.fixeol=True
kdiff3.gui=True
kdiff3.diffargs=--L1 '$plabel1' --L2 '$clabel' $parent $child
kdiff3.diff3args=--L1 '$plabel1' --L2 '$clabel' --L3 '$plabel2' $parent1 $child $parent2
;kdiff3.diff3args=--L1 '$alabel' --L2 '$plabel1' --L3 '$plabel2' $ancestor $parent1 $parent2 -o $child
kdiff3.dirdiff=True

Sunday, November 25, 2012

libgdx and iOS deployment quest - Part III

I discovered that my version of libgdx is a bit outdated and I needed to sync things up with the latest and greatest that includes ikvm support.  Since I'm trying to do everything on the MacBook Pro, that means I need hg-git support.  This allows you to deal with a git repo (which is how libgdx is version controlled) from the Mercurial tool.

On Windows, this is fairly easy.  Instructions are here.

On a Mac, it's also pretty easy.  If you followed the instructions for installing TortoiseHg from my earlier blogpost, you can simply open up a terminal and run this: "sudo easy_install hg-git".  From there you need to edit the .hgrc file:

[extensions]
hgext.bookmarks =
hggit =

Verify things work by entering "hg help hggit.

Hat tip to this site.

In other news, I was able to get both Outbreak: Zombie Slots and Toyland Slots running on the MacBook Pro.  It was scary easy.  libgdx is awesome!!!

Obligatory screenshot below:


libgdx and iOS deployment quest - Part II


I got SourceTree installed and cloning my repos, but didn't care for how it operated since I'm used to the TortoiseHg work flow.  Additionally, I noticed that it didn't set up 'hg' to be run from the command line by default.  I could do a "hg clone" from the terminal which is sometimes useful versus using a GUI to do all the heavy lifting.

I did some more searching and came across this page: https://bitbucket.org/tortoisehg/thg/wiki/developers/MacOSX. I came across this before as I was looking at my Hg options but didn't pursue it because the steps look way too involved and I wanted a pre-packaged approach to getting things running.  It turns out there are actually several different approaches at getting TortoiseHg running that are listed on this page... they just aren't presented very well to make you realize that.

At any rate, if you scroll down, you'll see instructions for installing things using HomeBrew.  They were very easy and very straightforward.  I only had a couple of hiccups where I tried to run the command and the instruction failed because it expected that I was running as root instead of a standard user.  Boiling things down, here's what I did:
  1. Install Xcode from App Store
  2. ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
  3. brew doctor
  4. Open Xcode → Preferences → Downloads → Install command line tools
  5. Set up .bash_profile as specified on the Wiki page (BREW setup, etc.)
  6. brew install pyqt
  7. brew install qscintilla2
  8. sudo easy_install pip
  9. sudo pip install Pygments iniparse Mercurial
  10. hg clone http://bitbucket.org/tortoisehg/thg ~/Documents/TortoiseHg
  11. cd ~/Documents/TortoiseHg
  12. ./thg
Success! Although it doesn't appear to have Finder integration, like the Windows version and its Windows Explorer integration, it does work and present me with the TortoiseHg Workbench I'm extremely familiar with.

Success!

Next up, to see if I can get my Outbreak: Zombie Slots running with Eclipse on the Mac platform.

libgdx and iOS deployment quest - Part I


After seeing the successful deployment of Noblemaster's libgdx-based games to the iTunes store, I figured it would be fun to port the libgdx games that I've created to iOS and release them to the iTunes store as well. So, today I went out to the local Best Buy and picked up a Macbook Pro 15” MD103LL/A.  It was on sale for about $120 less than the retail price that was listed.  I could have went for the more expensive model that included more system memory, but after doing some reading, saw that adding memory is pretty simple and cost effective so I'll probably go that route.

I brought the machine home and the first order of business was to get it running on my network.  Fail.

First and foremost – for whatever reason, the MacBook Pro would knock my Netgear WND3300 Wireless router out of commission. The net result (heh!) was that not only could the Apple machine NOT get on the Internet, but it also fouled up the rest of my network. Other machines that were working just fine would no longer be able to access the Internet. This is not the first time I've seen this behavior.

My brother-in-law has had a MacBook Pro for a while now. Every time he has brought his machine over, he has caused the same issues... after setting up the wireless configuration, boom – my network goes down. The only way to correct this in the past has been to reset the router.

This time I was determined to figure out a workaround if not an out and out fix. Using Google was worthless.  None of the tips I saw worked out.  After poking around with “ifconfig” and messing around with general settings, I noticed that I could get things to work once again if I manually defined the DNS values. My standard test to verify is simply “ping www.google.com” from a terminal window.

Now, normally, the DHCP server capabilities of the router kick in and assign the router as the DNS server for the DHCP client. This has worked flawlessly with Windows machine. For some odd reason, this causes issues using an Apple machine. I logged into the WND3300 and took a look at its DNS capabilities. It's cable modem side acts a DHCP client itself, which gets its configuration from the cable configurations that are assigned by the cable company. I looked at the assigned DNS server values. They looked correct. Based on a hunch, I modified the “Domain Name Server (DNS) Address” setting on the Basic Settings page from “Get Automatically From ISP” to “Use These DNS Servers” and specified what the cable modem always gives me. After doing that, the MacBook Pro no longer has any issues accessing the Internet and the other machines on the network similarly work fine. Whew! That blew about an hour or so of time.

Next on the list was to download Xcode. Xcode is the IDE used for developing iOS applications. I've used it a little bit in the past. I had to log into the Apple App Store and download it. Annoyingly, I also had to update my credit card info on file with Apple. As great as everyone claims Apple is, I find this to grate on my nerves... Especially given that Xcode is available as a free download. I guess I should also mention, I already had an Apple ID that I created back when I had an iPod Touch.  So, getting that stuff up and running was annoying but not world-ending.

With Xcode downloaded, I needed to install Eclipse. Wow. Installing Eclipse is easy. The problem comes in that there are about 10 different flavors of Eclipse. Historically, I've always gone for Eclipse Classic. However, this only provides a bare minimum. After doing some looking around, I discovered that there is a version of Eclipse for Mobile Developers. Sweet! It includes Java and C development tools. The libgdx library backend includes certain things written in C for performance reasons (the Box2D Physics Engine, for example.) I've had to make my own tweaks to that in the past so its been cool to have C tools available. At any rate, I installed that.

Then, I tried to run Eclipse. Blah. You need the Java JDK before it will work. On a Windows machine, you get that from Sun. Not so on an Apple machine. At a terminal prompt, I entered “javac -version” and it prompted me for an install. I wanted to specify an exact version of the JDK but I didn't see an option. I've heard of problems with JDK 7 and wanted to stick with JDK 6. I bit the bullet and just accepted whatever version the default installer selected. Turns out that I lucked out and am now running 1.6.0_37. Woot!

Next up I wanted to get my Mercurial repos running. On Windows, I use TortoiseHg which has really nice integration with Windows Explorer. On the Mac, there were a few options and I decided to give Atlassian's SourceTree a shot. I've got my repos hosted on Bitbucket so it seemed to make sense since BB is operated by Atlassian.  (Note: There has been an update on this -- I'm now using TortoiseHg on my Macbook Pro.)

In order to access my BB repos, I apparently need to get the ssh keys configured. I tried to follow Atlassian's directions where they imply that you'll be prompted to enter your ssh key info when you try to clone a project. This will supposedly add your key to your system's keychain. That's not the case. Trying to clone a repo resulted in an invalid URL problem and I couldn't get any further than that.

So, I copied my private BB ssh key over to the MacBook. In order to access my repos, I need to have my ssh key in the Mac's key ring. This is done via “ssh-add -K /path/to/key/here”. So I did this, got prompted for the passphrase and then.. it failed. Mwah? I generated the key using Puttygen and use it on nearly a daily basis, so I'm 100% the passphrase was being entered in correctly. Turns out that you need to use PuttyGen to export the key in an openSSH format. After doing that, I could add the key successfully.

Next up was to get my graphical development environment going. This meant getting Inkscape and GIMP up and running. Since I'm running OS X 10.8 (Mountain Lion), this is not as straightforward as simply installing the related dmgs. No. Trying to run Inkscape gives you a prompt saying “Where is X11?” Turns out that you need to first install Xquartz, which is the Open Source X11 that Apple no longer includes on its machines. Yippee.

After downloading that and rebooting, and trying to Inksape again, I was still prompted for “Where is X11”. Mwah? You need to go to your Utilities folder and start Xquartz and THEN start Inkscape. Geez. So much for the “ease” of using Apple products. On the other hand, the GIMP version was already setup with Mountain Lion compatibility so I didn't need to do much there other than copying the GIMP application to my Applications folder. Sweet.

Saturday, September 1, 2012

Stupid AdMob tricks

I use AdMob exclusively for my applications.  There's no particular reason other than it is a lot easier to set things up than having to futz with three or four different ad company SDKs, and having a single location to track my voluminous income (heh) is just super-convenient.

Today, I set things up so that all my apps will serve house ads for the duration of the (US) holiday weekend.

What's an AdMob House Ad?  An AdMob house ad allows you to serve up *free* ads in your applications.  You can do things like specify how many impressions you want to serve, which applications will display which ads, etc. The ads are restricted to applications you define, but otherwise free you from having to pay AdMob for them to appear.

At a high level, I've got two ad configurations defined for every app.  One version serves the Google Play link and the other version serves the Amazon App Store link.  The ad that gets served is based on the device that ad is appearing on.

Specifically, here's how I set things up:

1) I created a house ad campaign called "all house ads".  At this level in AdMob, you are able to define the start and end times.  I've got things setup to run from today until midnight Monday (end of the Labor Day holiday).  That's a total of three days of house ad rotation.

2) I created several ad groups under the "all house ads" campaign.  At this level in AdMob, you are able to define which apps the ad group will appear in and your impression goal. 

I defined separate ad groups for: DB42 Lite, DB42 Full Version, Doodle Bug, Drawccupations, MatchCard, Pyromancer's Laboratory, and Sky Ninja.

Now, I could have defined a single group and placed all of these ads in that group.  I did not go this route because I wanted fine tuned control over which apps my ads appear in. For example, I don't want the Sky Ninja ad to appear in the Sky Ninja app... The user is already playing Sky Ninja -- therefore I don't need to drive him or her to download that exact same app.


For each ad group in this section, I selected the impression goal of "Fully Allocate".  This means, I want to completely turn my ad mediation over to my house ads.  In other words -- don't serve up any paid ads!  I'm trying to drum up support in my apps for my other apps, so that's why I'm bailing on the paid ads for this duration.

I wasn't sure if the "Fully Allocate" configuration would limit the ad group to a single group or not.  Through experimentation, I see that the ads are rotating through all the ad groups I have defined for the campaign.  This is EXACTLY what I want.

It is also at this level that you can define target demographics and devices.  I initially set things up to serve all demographics and devices EXCEPT the Amazon Kindle Fire.  Ads served to the Kindle Fire are served somewhat differently because I want to reference the Amazon App Store link for those.  Keep reading...

3) For each ad in the group, I've got the URL defined for the app and the image to display with that URL.  I initially created URLs that pointed to the Google Play Market URL.  For example, the DB42 Lite URL points to:

market://details?id=com.gushikustudios.db42lite

4) After I set all of that up for the Google Play links, I copied every single Ad group, and added "Amazon" to the name (e.g. "Sky Ninja Amazon").  Then, I went into each Amazon ad group and edited the URL to point to the Amazon App Store's URL.  For example, the DB42Lite URL changed from what I list above to:

amzn://apps/android?asin=B006KU55UO

The 'asin' value is listed on the Amazon App Store's page for the app.

When you click on this app from the Kindle Fire, you open up the App Store view of your app instead of a the Amazon Retail Web Site.  This makes it easier for the user to install your app on a Kindle Fire for obvious reasons.

I'll post the results of doing all this in a few days.  Hope this helps someone else out there!

Sunday, August 26, 2012

Ant, Javadoc, Libgdx, CreateProcess error=87, and YOU!

Gratuitously copied from the Western Skies blog since I *just* got bit by this -- and apparently got bit by it before 'cuz Google showed that I had visited this link sometime in the past:

I've been developing an Ant build file for a large-ish base of existing code. I've been using my Mac as the primary devleopment platform, but the ultimate target (for the other developers) is Windows. My task to build the javadocs runs fine on OS X, but the first time I rant it on Windows, I got the following:

c:\demo\build.xml:180: Javadoc failed: java.io.IOException: Cannot run program "c:\Program Files\Java\jdk1.6.0\bin\javadoc.exe": CreateProcess error=87, The parameter is incorrect

After about 10 minutes of Googling, I found a/the simple solution: add the useexternalfile attribute to the javadoc task in Ant. Voila , back in business, on the PC at least, and it didn't seem to break the build on the Mac.

 I got bit by the most recent version libgdx when trying to run "ant" in order to build the "dist", which includes the API documentation that I was really aiming for.

Monday, August 20, 2012

3D Math Primer for Graphics and Game Development, 2nd Edition

I've picked up a copy of 3D Math Primer on Graphics and Game Development recently and have been slowly going through it.  It's been around 15 years since I took Linear Algebra, or had to do any real matrix or vector based math, so the brain cells are slowly being re-initialized.  Heh.  Hopefully, this turns into a series of posts to share as I relearn things cruising through the book.

Here's what I've covered so far.

Chapter 1 deals with coordinate systems and concepts.  I was always fairly decent at trig so this went by really quick.  Chapter 2 gets into vectors and discusses the importance of the dot and cross products.  I remember learning and using these in school, but their application in game development terms wasn't obvious to me.  There are a couple of really good exercises that hit home the importance of these two operations.

One of the exercises describes an NPC in a game at a point p, that has a forward vector v.  Given this, and a point x, how can you use a dot product for determining whether or not the point x is in front or behind the NPC?

There are a couple of things to know about the dot product.  First, it gives you a scalar.  Big whoop.  How can this be used?  Well, the sign of the scalar gives you an indication of the relative direction / angle between one vector and another.  If the dot product is positive, then the angle between them is between 0 and 90 degrees.  If the dot product is 0, then the vectors are orthogonal (ie. at right angles).  If the dot product is < 0, then the angle between the vectors is greater than 90 degrees (and less than 180).

So, given a NPC location of p, we can calculate a vector x-p that gives us a vector describing the relative position of point x from point p.  If we know the facing of the character (vector v at point p), then we can take the dot product between v and x-p and know the relative positioning.

Recall that the dot product yields a scalar, defined as:

a dot  b =
(ax*bx + ay*by + az*bz).

Given this, we can determine if point x is in front-of or behind the NPC:

If vx*(xx-px)+vy*(xy-py)+vz*(xz-pz) > 0, it's in front.  If it's < 0, is behind.  If it's == 0, then it's to the side of the NPC.

Cool!

Using the magnitudes of the related vectors and the relation: cos theta = a dot b / (||a|| * ||b||), we can further determine the angle between the two vectors.

Another cool exercise from the book details the practical application of the cross-product.  If the NPC above navigates from point p to point q and then to point r in the x-z plane (using left hand orientation), how can we use a cross-product to determine if the NPC should turn clockwise or counter-clockwise at point q in order to reach r?

Recall that the cross-product yields a new vector, defined as:
a x b =
[ax ay az] x [bx by bz] =
[ay*bz-az*by -(ax*bz-az*bx) ax*by-bx*by]

Here, it helps to know that the cross-product for a couple of vectors yields another vector that is orthogonal to the other two vectors.  This vector gives a magnitude that is proportional to the area of the parallelogram area formed by the vector sides.  We can look at the direction of the vector to clue us in to whether the orientation from one vector to another is clockwise or counter-clockwise.  If the angle from a to b is less than 180 degrees, the resulting vector will point up (left-hand rule orientation) on the orientation is considered clockwise.  If the angle from a to b is greater than 180 degrees (ie.  from b to a is less then 180 degrees), the orientation is considered counter-clockwise and the resulting vector will point down.

In the NPC case above, it's pretty straightforward to calculate the cross-product.  Doing this results in a single vector that either points in the +y direction, or the -y direction. Using this info, we can easily determine which way to rotate the NPC at the point q.  The NPC could actually rotate either way and still reach the same position.  The cross-product ends up giving you the shortest direction to rotate through.

Cool x2!


PDF Xchange Viewer Fu

I do a lot of PDF technical document reading where I'm constantly flipping back and forth between  various document sections.  Although my favorite PDF viewer of choice, PDF XChange Viewer, rocks the house for annotating, searching, saving, etc., it sucks when it comes to intra-document navigation.  It's got bookmarks and capabilities to run multiple instances, but I haven't liked the navigation these implement.  I've also tried using multiple copies of my documents and gotten that to work the way I liked (ie. tabbed document browsing), but having multiple copies seems like a real waste to me.

Enter Chrome.

Using Chrome tabs, you can easily and quickly open up multiple instances of a document by using the address bar to navigate to the local file.  From there, just right-click and select duplicate. You end up creating multiple instances of PDF XChange Viewer in this case, but the navigation is SOOOO much better than clicking on the Windows taskbar.

You may have to disable the built-in PDF Viewer in Chrome to get PDF XChange Viewer working.  To do this, enter "chrome://plugins" and simply disable "Chrome PDF Viewer".

QED.

Sunday, May 6, 2012

SVN -> Mercurial mirroring

I'm continuing to use libgdx for my game development.  However, locally I'm using Mercurial for software configuration while libgdx is currently stored up on GoogleCode using SVN.  Ugh.  How to stay in sync or otherwise mirror the SVN repo?  So far, I've been maintaining things manually.  The approach I take for pulling in SVN updates is outlined on the Selenic site here:

http://mercurial.selenic.com/wiki/SubversionToMercurialHowto

This boils down to keeping a single directory that serves both as a SVN *and* Hg working directory.  From this directory, I do a clone per project to set things up.  For example, I have a clone of libgdx for my MatchCard game, another clone for Doodle Bug, another clone for Pyromancer's Laboratory, and yet another one for DB42 and DB42Lite.

The nasty part of this business is that means I have to go to my local SVN repo, perform an SVN update, and then do an add/remove/commit on the Hg repo.  Since this is a manual operation, it doesn't happen all that often.  I'm between games right now and wanted to more or less set things straight in terms of keeping in sync without having to rely on a manual process.  Here's the solution I came up with:

1) I'm sticking with the shared SVN / Hg working directory.  This is really straightforward and I have my brain completely wrapped around what's going on.

2) I've got cygwin installed on my Win7 x64 workstation to give me all the great tools that Unix developers know and love... grep, head, tail, bash scripting, etc.

3) I've got Jenkins installed on this same machine.  This does periodic job runs that query the state of the SVN repo, pull in new updates, and then add/remove/commit those changes to the Hg side of the shared directory.  The build trigger is simply "Build Periodically" that runs the following script:

for ((COUNT=`svn log -r base:head | grep -E "^r[0-9].*" | wc -l`; COUNT > 1; COUNT-- ))
do
   GETREV=`svn log -r base:head -l 2 | grep -E "^r[0-9].*" | tail -n1`
   TOKENS=( $GETREV )
   REV=${TOKENS[0]}
   AUTHOR=${TOKENS[2]}
   COMMENT=`svn log -r $REV | tail -n +4 | head -n-1`
   CHECKINCOMMENT=$(echo -e "$COMMENT\nSVN $REV")
   svn update -r $REV
   hg addremove
   hg ci -u "$AUTHOR" -m "$CHECKINCOMMENT"
done
What this script does is read the number of changes between the local base and the GoogleCode head.  If it is > 1 (ie. there are new changes), it iteratively pulls each SVN changeset and then commits those to the Hg repo using SVN revision, author, and comment info pulled from SVN's log command.  This took a while to get going -- my bash scripting skills are extremely rusty.

The one thing I'm lacking at this point is an "hg push" up to a default repo out on the Intertubes.  I have it in mind to share the repo on Assembla, but I currently don't have the libgdx's team blessing to do this (yet) and I also have a support request into the Assembla team to see if they have any server side tools that might accomplish the same thing I'm doing locally.  This whole process is highly reliant on my build server not dying.  If I could push those operations out to some other server, that'd be better in the long run.  For now, though, it works

Hope this helps someone trying to accomplish something similar.