Categories
iOS Development Programming

Expanding SMXMLDocument

Just a quick post here due to time constraints.

I don’t have time to submit this properly through GitHub because I haven’t cloned the repo, but I did want to share it since finding and using this class saved me a few hours of effort.

This is a small extension to SMXMLDocument (a very useful iOS XML parser, thanks, Nick) which will return all children for a given path (specified as an array of strings), not just the first match it finds.

- (NSArray *)descendantsWithPath:(NSArray *)path {
  NSMutableArray *lineage = [NSMutableArray arrayWithArray:path];
  NSMutableArray *array = [NSMutableArray array];

  NSArray *kids = [self childrenNamed:[lineage objectAtIndex:0]];
  [lineage removeObjectAtIndex:0];

  if ([kids count] > 0) {
    if (0 == [lineage count]) {
      // bottom of path
      [array addObjectsFromArray:kids];
    } else {
      // recurse into path
      for (SMXMLElement *el in kids) {
        NSArray *elements = [el descendantsWithPath:lineage];

        if ([elements count] > 0)
          [array addObjectsFromArray:elements];
      }
    }
  }
  return array;
}

This can be easily extended to:

- (NSArray *)descendantsWithPath:(NSArray *)path andAttribute:(NSString *)attribute

To find only leaf nodes on the given path with a specific attribute, but I haven’t gotten that far in my own project yet–possibly a future update to this post.

Note: This was based on the master-arc branch supporting ARC.

Enjoy!

Categories
Programming

Automating Shared iTunes Library Access

I’ve been sharing my iTunes library in-house with multiple Macs reading from the same library on a shared drive for about three months now.

Most of the time iTunes is running on a MacMini server in the office and access to the single library containing all our content is done through the iTunes Home Sharing. This work great (most of the time) when all we are doing is playing music or videos. Things get more difficult when I want to add some content to the library from the MacBook.

The major issue I keep running into is iTunes’ lock of the library file and the need to shut down the server instance of iTunes. This requires a trip to the office or a remote login to the server. Not a huge bother, but more work than it ought to be, IMHO.

I figured there had to be a way to simpify things and after several hours of research, help from the TheMacTipper, Daring Fireball, and a lot of trial-and-error, I’ve crafted a solution which runs with only a click or two with the help of my DropBox account.

Folder Actions Fail

Since I was using a shared folder for my iTunes library I started off thinking that the solution would entail the use of some AppleScript and Folder Actions. I was half right. The AppleScript is required, but the Folder Actions weren’t up to snuff.

I put together a Folder Actions script which would shut down iTunes when triggered to do so. I figured an easy trigger would the existence of a file with a specific name, say “iTunesQuit”. Simple enough. And it worked. Sort of.

First there was the issue of folder actions not being reliable. So I decided to research using LaunchD instead.

Second, it turns out that the afp: protocol which defines the shared mount has an indeterminate lag when syncing writes to the disk. This lag was longer than I was willing to wait within a scripted action. When I need access to my iTunes library, I’d like it to happen quickly, not in twenty or thirty seconds.

Dropbox to the Rescue

Since writing a file to a shared drive was too slow I started to look at the other ways I share data between computers and I was immediately drawn to DropBox. The key feature in this solution is the “Enable LAN Sync” option which Dropbox uses to reduce network traffic to its servers.

It turns out that Dropbox is pretty quick on the draw with this LAN sync and I could script a wait of mere seconds–more than fast enough for what I wanted to accomplish.

Dropbox had the added benefit of making the solution presented below portable as well. The AppleScript to control things could be saved in a Dropbox folder and referenced from any machine configure to sync with Dropbox.

The Solution in Four Parts

Since I was automating the shutdown of the iTunes instance on the office server I thought I could do the reverse and automate the startup of iTunes on the server once I was done accessing the library from the MacBook. My need to get the server in the office running iTunes again is not as urgent so I use a little longer delay in coordinating this action.

So my ultimate solution is comprised of four parts:

  1. iTunesControl.scpt AppleScript to control things.
  2. com.wh1t3s.iTunesControl.plist Lauch Agent to invoke AppleScript above as needed
  3. _iTunes_.app Automator application to trigger remote shutdown
  4. iTunesOnServer.app Automator application to trigger remote iTunes restart

iTunesControl.scpt

I want to preface my code here with the caveat that this is the first AppleScript I’ve ever written. There may be simpler, more elegant, or simply more correct ways to do the things I am doing, but I stopped my development at what worked for me. (Please kindly leave suggestions for improvement in the comments below, preferably sans judgement.)

This is the script which is tied to the LaunchD launch agent created to watch the Dropbox folder (/Users/myUserName/Dropbox/iTunesSync) I am using to trigger my iTunes actions: existence of “iTunesQuit” will shut down iTunes on any machine configured with the launch agent, existence of “iTunesRun” will activate iTunes on the named server, while also shutting down other iTunes instances by creating “iTunesQuit”.

Please remember to change myUserName and MacMiniServer items below with comparable items suitable to your implementation.

--
-- saved as /Users/myUserName/Dropbox/iTunesControl.scpt
--
property quitFile : POSIX file "/Users/myUserName/Dropbox/iTunesSync/iTunesQuit"
property runFile : POSIX file "/Users/myUserName/Dropbox/iTunesSync/iTunesRun"

on run
  try
    set isRunning to appIsRunning("iTunes")

    tell application "Finder"
      if exists quitFile then
        if isRunning then
          tell application "iTunes" to quit
        end if

        -- delay to allow Dropbox to complete
        delay 5

        -- check existence again in case another Mac already deleted it
        if exists quitFile then
          move quitFile to trash
        end if

      else if exists runFile then
        if "MacMiniServer" is equal to computer name of (system info) then
          -- delete runFile
          move runFile to trash

          -- trigger remote iTunes shutdown
          do shell script "touch /Users/myUserName/Dropbox/iTunesSync/iTunesQuit"

          -- delay while any other instances of iTunes are shutdown
          delay 15

          -- start iTunes on server
          tell application "iTunes" to activate
        end if
      end if
    end tell
  end try
end run

on appIsRunning(app_name)
  tell application "System Events"
    set app_list to every application process whose name is equal to app_name
    if the (count of app_list) > 0 then
      return true
    else
      return false
    end if
  end tell
end appIsRunning

com.wh1t3s.iTunesControl.plist

I actually created this launch agent with Lingon since this was my first attempt at launch agents. I will save you the new and improved cost of the app in the new Apple Mac App Store of $4.99 and post the resulting plist file in its entirety. This file was saved as /Users/myUserName/LIbrary/LaunchAgents/com.wh1t3s.iTunesControl.plist.





    Label
    com.wh1t3s.iTunesControl
    ProgramArguments
    
        osascript
        /Users/myUserName/Dropbox/iTunesControl.scpt
    
    QueueDirectories
    
    WatchPaths
    
        /Users/myUserName/Dropbox/iTunesSync/
    


_iTunes_.app

This is a very simple Automator application; a shell script to create the iTunes shut down trigger file, wait a few seconds, then start iTunes.

iTunesOnServer.app

An even simpler Automator application; a script to create the server run iTunes trigger file.

Making it All Work

To bring it all together:

  1. Install the Dropbox client on all machines.
  2. Save iTunesControl.scpt to ~/Dropbox/ (or wherever your Dropbox folder is located, I put mine in my home directory.)
  3. Save com.wh1t3s.iTunesControl.plist to ~/Library/LaunchAgents on all machines.
  4. Copy _iTunes_.app to all machines.
  5. Copy iTunesOnServer.app to all machines but the server.

To start iTunes on the server, start the app iTunesOnServer on any machine. To shutdown the server instance and run iTunes on another machine, start the _iTunes_ app on that machine. Shared instances point to the same iTunes library on the shared disk.

This solution is working for me for a few days now, YMMV.

Categories
Programming

How to Setup a Dell Precision M6500 for GPGPU Development with FC13

Disclaimer

This post will hopefully save someone a few hours of trial-and-error. It is a result of three separate attempts in a 12-hour period to get things up and running myself. I believe that my notes are complete, but please remember, you are getting the information for free so YMMV! If it doesn’t get you a 100% solution hopefully it gets you 90%. Just so you are aware up front–this process as documented is for a 32-bit Fedora Core 13 installation with the latest NVIDIA drivers (v260.19.12 at the time of writing.) The graphics card on my M6500 is the NVIDIA Quadro FX 2800M. I expect the process would not be much different any other CUDA-capable NVIDIA card. And one last point, if you are afraid of, or enable to use, the command line then this is not the post for you.

Installing FC13

I started with the FC13 LiveCD since I needed to verify that the certain peripheral drivers worked for out-of-the-box. I am not a big fan of rebuilding a kernel unless it is absolutely necessary and like to start with as complete a solution as I can. After verifying the drivers needed were present I did an install-to-disk from the LiveCD. This was done via the icon on the LiveCD user desktop.

Once the install is complete, eject the CD and reboot the system. When the system returns, complete the install setting the root password, creating a user (for the purpose of this post that name of that user will be ‘me’,) etc. Once the setup tasks are complete I reboot again for good measure. I then login as ‘me’.

I find it useful to add myself to the sudoers list to allow sudo access without requiring a password. Do this from a terminal (Applications | System Tools | Terminal):

[me@m6500 ~]$ su -
Password:********
[root@m6500]$ cat >> /etc/sudoers
me ALL=(ALL) NOPASSWD: ALL

[root@m6500]$ exit
logout
[me@m6500 ~]$

This simplifies things moving forward since much of the following requires root privs and would require numerous password entries to complete. This configuration allows sudo usage without a password, moving things along a little quicker. It also means that I can stay logged in as ‘me’ to accomplish everything.

The next thing I like to do is disable the firewall, which is enabled in the default install. Using System | Administration | Firewall allows the firewall to be disabled (after entering the root password.)

One last step may be required before we get to updating the default installation with yum and it depends on your network configuration. If you have a proxy server in place you need to let yum know about it. I prefer to do this in the /etc/yum.conf file:

[me@m6500 ~]$ sudo cat >> /etc/yum.conf
proxy=http://:

[me@m6500 ~]$

Of course, set and to values consistent with your network configuration.

The system is now ready to invoke yum to update the default install. This is done with the following command:

[me@m6500 ~]$ sudo yum update
.
.
.
[me@m6500 ~]$ 

In my instance, there were over 450 updates to be applied and this process takes quite a while. Be patient. In the mean time, we can do some parallel processing and download the driver, toolkit and SDK sample code from NVIDIA. These are the links I used for the 3.2 RC version of things (full paths are included in case you want to work from a hardcopy of this post):

NVIDIA Downloads Page – http://developer.nvidia.com/object/cuda_3_2_toolkit_rc.html

Driver – http://developer.download.nvidia.com/compute/cuda/3_2/drivers/devdriver_3.2_linux_32_260.19.12.run

Toolkit – http://www.nvidia.com/object/thankyou.html?url=/compute/cuda/3_2/toolkit/cudatoolkit_3.2.9_linux_32_fedora13.run

SDK Samples – http://developer.download.nvidia.com/compute/cuda/3_2/sdk/gpucomputingsdk_3.2_linux.run

You should ensure you are getting the latest (unless you are trying to replicate my install) from NVIDIA here:

NVIDIA – http://developer.nvidia.com/page/home.html

I saved all the downloads to my home folder (~me or /home/me in my case.) Once the yum update is finished and all your files are finished downloading it is time to once again reboot. The installed updates include a kernel update so a reboot is required prior to driver installation so we do not apply the driver to the current kernel but instead to the freshly updated kernel.

Since some X drivers are going to be installed, it is now time to shutdown the X-server and login at the command line. You can do this either in the above reboot by editing the boot command line and entering a ‘3’ at the end (indicating you want to boot to runlevel 3,) or once the GUI boot is complete, login, start a terminal and enter the command:

[me@m6500 ~]$ sudo init 3
.
.
.
m6500 login: me
Password: ********
[me@m6500 ~]$ 

Login to your user account (‘me’ in my case) once you see the command line login prompt. We can now move on to completing the install.

Installing the NVIDIA Pieces

FC13 ships with a default open-source driver for NVIDIA cards called nouveau. Unfortunately, installing over this driver is more complicated than simply following the default driver installation instructions from NVIDIA. Fortunately for you, others before me (reference links: http://fedorasolved.org/video-solutions/nvidia-yum-kmod, and http://forums.fedoraforum.org/showthread.php?t=204752) have done the hard work and I am passing on their knowledge in a more complete form (as it fit my purposes, at least.)

Based on the references above, I created a few scripts (also located in ~me.) The first, do-nvidia.sh, adds RPMFusion repositories to the yum.conf, installs a few NVIDIA packages from RPMFusion, rebuilds the initrd image, and reconfigures grub to override the default nouveau driver.

do-nvidia.sh

#!/bin/bash

# add RPMFusion repositories
rpm -Uvh http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm
rpm -Uvh http://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-stable.noarch.rpm

# install nvidia from RPMFusion
yum install kmod-nvidia xorg-x11-drv-nvidia-libs.i686

# blacklist nouveau driver from initrd in grub.conf
sed -i ‘/root=/s|$| rdblacklist=nouveau vmalloc=256M|’ /boot/grub/grub.conf

# regen initrd
mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r)-nouveau.img
dracut /boot/initramfs-$(uname -r).img $(uname -r)

This script must be invoked with sudo:

[me@m6500 ~]$ sudo ./do-nvidia.sh
.
.
.
[me@m6500 ~]$

The second script, do-installs.sh, installs development packages required for the NVIDIA GPU Computing SDK (and a couple of GUI config utilities I find useful):

do-installs.sh

#!/bin/bash

# install development stuff
yum install kernel-source kernel-devel
yum install gcc gcc-c++
yum install mesa-libGLU-devel
yum install libXi-devel
yum install libXmu-devel
yum install freeglut
ln -s /usr/lib/libglut.so.3 /usr/lib/libglut.so

# install misc
yum install samba
yum install system-config-samba
yum install system-config-network
yum install system-config-services

This script is also invoked with sudo:

[me@m6500 ~]$ sudo ./do-installs.sh
.
.
.
[me@m6500 ~]$

Next we move on to installing the CUDA Toolkit. I chose to use the default install paths for everything, and this and any future posts will reflect this. So the toolkit, by default, gets installed in /usr/local/cuda:

[me@m6500 ~]$ sudo ./cudatoolkit_3.2.9_linux_32_fedora13.run
.
.
.
[me@m6500 ~]$

Once the toolkit is installed we can move on to the GPU Computing SDK. This can be a local install in a single user directory, so as invoked below using default paths, it installs to /home/me/NVIDIA_GPU_Computing_SDK:

[me@m6500 ~]$ cd && pwd
/home/me
[me@m6500 ~]$ ./gpucomputingsdk_3.2_linux.run
.
.
.
[me@m6500 ~]$

In the reference material I found, there were indications that one did not have to run the devdriver install from NVIDIA. My experience was that I was missing libGL and found reference to the fact it is built by the driver install. I ran the install, YMMV:

[me@m6500 ~]$ sudo ./devdriver_3.2_linux_32_260.19.12.run
.
.
.
[me@m6500 ~]$

At this point the install should be complete. Invoking the SDK build, it should now succeed:

[me@m6500 ~]$ cd ~/NVIDIA_GPU_Computing_SDK/C
[me@m6500 ~]$ make && bin/linux/release/deviceQuery
.
.
.
Finished building all
bin/linux/release/deviceQuery Starting...

 CUDA Device Query (Runtime API) version (CUDART static linking)

There is 1 device supporting CUDA

Device 0: "Quadro FX 2800M"
  CUDA Driver Version:                           3.20
  CUDA Runtime Version:                          3.20
  CUDA Capability Major/Minor version number:    1.1
  Total amount of global memory:                 1073020928 bytes
  Multiprocessors x Cores/MP = Cores:            12 (MP) x 8 (Cores/MP) = 96 (Cores)
  Total amount of constant memory:               65536 bytes
  Total amount of shared memory per block:       16384 bytes
  Total number of registers available per block: 8192
  Warp size:                                     32
  Maximum number of threads per block:           512
  Maximum sizes of each dimension of a block:    512 x 512 x 64
  Maximum sizes of each dimension of a grid:     65535 x 65535 x 1
  Maximum memory pitch:                          2147483647 bytes
  Texture alignment:                             256 bytes
  Clock rate:                                    1.50 GHz
  Concurrent copy and execution:                 Yes
  Run time limit on kernels:                     Yes
  Integrated:                                    No
  Support host page-locked memory mapping:       Yes
  Compute mode:                                  Default (multiple host threads can use this device 
simultaneously)
  Concurrent kernel execution:                   No
  Device has ECC support enabled:                No
  Device is using TCC driver mode:               No

deviceQuery, CUDA Driver = CUDART, CUDA Driver Version = 3.20, CUDA Runtime Version = 3.20, NumDevs 
= 1, Device = Quadro FX 2800M


PASSED

Press  to Quit...
-----------------------------------------------------------

[me@m6500 ~]$

Success!!! (Hopefully your installation is successful as well.

This post is long enough. I will follow it shortly with a post describing how to setup your own Makefile-based CUDA projects based on the NVIDIA GPU Computing SDK.

So long for now.

A Quick Follow-up

After looking closer at the boot logs I noticed I had an error:

Checking for module nvidia.ko: ESC[60G[ESC[0;31mFAILEDESC[0;39m]^M
nvidia.ko for kernel 2.6.34.7-61.fc13.i686 was not found.ESC[60G[ESC[0;33mWARNINGESC[0;39m]^M
The nvidia driver will not be enabled until one is found.ESC[60G[ESC[0;33mWARNINGESC[0;39m]^M
*** glibc detected *** /usr/bin/python: free(): invalid pointer: 0x00902822 ***
======= Backtrace: =========
/lib/libc.so.6[0xbc9fe1]
/usr/lib/python2.6/site-packages/ixf86configmodule.so(+0xdfe7)[0x8fcfe7]
/usr/lib/python2.6/site-packages/ixf86configmodule.so(xf86freeConfig+0x8c)[0x8f97fc]
.
.
.

I did two things two correct these errors. First I fixed the path in /etc/init.d/nvidia, adding:

elif test -e “${modpath}/kernel/drivers/video/${modname}”;then
module=”${modpath}/kernel/drivers/video/${modname}”

at line 28. This is where my nvidia.ko module landed–once this was added the “module not found” message disappeared, but I still had the invalid pointer error.

The second thing I did was a result of reading this (http://fedoraforum.org/forum/showthread.php?t=248592). Following the steps in post #4 of this thread:

[me@m6500 ~]$ sudo rm -f /etc/X11/xorg.conf
[me@m6500 ~]$ sudo nvidia-config-display disable
[me@m6500 ~]$ sudo nvidia-config-display enable

cleared up the remaining errors. The system now boots with no errors.

Categories
iOS Development Products Programming

A New App — Four³

After investing in some tools, a few months of working evenings, and a ten day wait in the App Store approval queue my second iOS app is now available.

Initial Thoughts

The Easter Bunny was kind enough to wait in line on release day at my local Apple store and deliver to wife a shiny new iPad. When I saw the resolution and clarity of the screen I knew I wanted to do a game for it. After looking for an hour at the simple games that already existed in the app store, it occurred to me that–since some of the apps had set the bar pretty low–it shouldn’t be too hard to improve on a graphical 3D Tic Tac Toe. And since 3x3x3 Tic Tac Toe is no challenge what-so-ever, I decided to go to 4x4x4–hence the name of the game Four³.

Four³ is a true 3D, four-in-a-row implementation of Tic Tac Toe.

Tools

My initial thinking was to do the game in OpenGL (which led to my previous post) but as I researched what it would take to implement my game it became apparent that using OpenGL would require me to do more than just the graphics and I was really hoping to get something into the App Store sooner rather than later. Since I didn’t want to give up all my family time, and my App Store exposure is not (yet) great enough for me to quit my day job, I decided to invest in some game development tools to simplify the process.

I googled 3D game engines and wound up selecting Unity3D. After downloading the demo (and a gracious extension of said demo after a two week halt in development due to the uncertainty of the new iOS 4 TOS) I was able to almost fully prototype my application with only the demo license. At that point I was convinced the $300 cost of the Unity iPhone basic license was warranted. Although games based on the Unity engine are still being approved in the App Store the folks at Unity3D are working hard on a workaround to reduce the ambiguity about the new terms of service and the use of a tool like Unity.

The Game Board

My 4x4x4 tic tic toe game board is simply defined by 9 intersecting planes which delineate 64 game spaces. These planes are easily fabricated.

The game tokens were also easy to generate–the “O” token is simply a sphere and the “X” token is six carefully arranged smaller cubes combined into a single “prefab”.

So the game board and the player tokens were the easy part; more complicated was determining how to allow players to select a game space. In my prototyping phase I realized it would be a simply matter to allow the player to choose a smaller sphere–a “move dot”– which is pre-populated in a game space. The Unity engine allows the kinematics to be defined on an object-by-object basis, so it was a simple matter to configure the grid planes not to respond to touches and configure the “move dots” to do so. Essentially the translucent planes defining the board are invisible to touches. (This is the type of thing that would have been much more time consuming had I gone straight to OpenGL.)

Suspending the game board in space was a simple matter of surrounding it with a skybox.

Since the game tokens can quickly fill any single plain of the game board, it was imperative that the user be able to rotate/spin the board to be able to view the unused game spaces.

The Game Play

It was easy to determine the set of winning vectors. It was a simple matter to track when either player had control of winning vector–control being defined as one player, but not both, having a token in the vector. It was easy to implement the two-player game as no AI was needed, other than determining when a draw had occurred. Implementing the AI for the more advanced device game play slowed me down a bit. I actually set things aside for about ten days to fiddle with some other development.

I decided to make the easy level really easy–on this level the device simply picks a random unused game space. This makes it very easy to beat the device as there is really no offensive or defensive strategy involved when the device chooses its next move.

The medium and hard levels present different combinations of offensive and defensive strategy. I will not be revealing the full details, but I will say that much of the decision making is based on how much control a given player has of a given win vector and the hard level presents a greater defensive strategy than the medium level. I was quite pleasantly surprised when, even as the developer, the hard level beat me the first two out of three games I played against it (I let the device go first.)

iPhone Input Sample Script

The primary purpose of this post is to return something to the Unity community. I learned an awful lot from the forums and other resources I discovered.

Below is a portion of my game script which deals with iPhone touch input. There was no single example available when I started my research that showed quite this much interaction so I am publishing this to help others with their Unity development.

The highlights in this script include:

  1. Detecting taps to select a move dot
  2. Detecting a swipe to rotate the game board
  3. Detecting pinches to zoom in and out

I hope someone finds it useful.

Example.js

//
// Control all user interactions here
//

#pragma strict
private var touchBegan: boolean;
private var previous: Vector2;
private var swipe: int;
private var dx: float;
private var dy: float;
private var dVec: Vector3;
private var minDist: float; 
private var maxDist: float;
private var moveFactor: float;
private var minMajorDist: float;
private var curDist: Vector2;
private var prevDist: Vector2;
private var	touch2: iPhoneTouch;	
private var nTouch;
private var dpos: Vector2;
private var pinch: boolean;
private	var slide: float;
private var nextDeviceMove: GameObject;
private var go: GameObject;
private var pos: Vector3;
private var hit: RaycastHit;
private var currentPlayer: int;
private var undoLimit: float;
private var undoAllowed: boolean;
private var undo: boolean;
private var devicePlays: int;
private var timeSinceLastMove: float;
private var iPhoneInUse: boolean;
private var myPosition: Vector2;
private var rotationRate: float;
static var touch: iPhoneTouch;
static var popup : boolean;
static var tap: boolean;

function Start() {
	resetGamePlay();	
	
	// currentPlayer and devicePlays set by settings screen
	Debug.Log("PlayGame() - currentPlayer ("+currentPlayer+")");
	
 	if (devicePlays && 2 == currentPlayer) {
 		DeviceMove();
 	}
}

function resetGamePlay() {
	touchBegan = false;
	swipe = 0;
	pinch = false;
	dVec = Vector3.zero;
	minDist = 16; 
	maxDist = 30;
	moveFactor = 0.05;
	minMajorDist = 15;
	maxMinorDist = 7; 
	popup = false;
	tap = false;
	defRotationRate = 45.0;
	rotationRate = defRotationRate;
	undoLimit = 2.0;
 	nextDeviceMove = null;
	orientationReset = iPhoneSettings.screenOrientation;
	undoAllowed = false;
	undo = false;
	
	transform.LookAt(Vector3.zero);
  
	iPhoneInUse = (iPhoneSettings.model.Substring(0,1) == "i");
}

//
// Check for iPhone Touches here
//
function FixedUpdate () {
	// 
 	// if the popup menu is visible don't do normal processing
	//
	if (popup)
		return;
		
	timeSinceLastMove += Time.deltaTime;
	
	if (undoAllowed && timeSinceLastMove > undoLimit) {
  		undoAllowed = false;

		Debug.Log("Time for device move "+timeSinceLastMove);
		if (devicePlays && 2 == currentPlayer) {
			DeviceMove();
		}
	}
	
	if (!iPhoneInUse) {
		// get position from mouse (for developemnt only)
		tap = Input.GetMouseButtonUp(0); 
		myPosition = Input.mousePosition;
		return;
	}
	
	//
	// Decode touches here
	// 
	nTouch = iPhoneInput.touchCount;
	if (nTouch == 1) {
		pinch = false;
		touch = iPhoneInput.GetTouch(0); 
 	  
		if (touch.phase == iPhoneTouchPhase.Began) {
			dvec = Vector3.zero;
			previous = touch.position;
			touchBegan = true;
			swipe = 0;
			tap = false;
		} else if (touchBegan && touch.phase == iPhoneTouchPhase.Moved) {
			dpos = touch.position - previous;
			dx = Mathf.Abs(dpos.x);
			dy = Mathf.Abs(dpos.y);
  	  
			if (dx >= minMajorDist && dy <= dx) {
				// swipe in x-axis
				swipe = (dpos.x<0) ? -1 : 1;
				previous = touch.position;
				dVec = Vector3.up;
			} else if (dy >= minMajorDist && dx <= dy) {
				// swipe in y-axis
				swipe = (dpos.y<0) ? -1 : 1;
				previous = touch.position;
				dVec = -transform.right;
 			}	
		} else if (touch.phase == iPhoneTouchPhase.Ended) {
			touchBegan = false;
			tap = (0 == swipe);
			swipe = 0;
		}
	} else if (nTouch == 2) {
		pinch = false;
		touch = iPhoneInput.GetTouch(0); 
 		touch2 = iPhoneInput.GetTouch(1); 
		dVec = Vector3.zero;
		  	
		if (touch.phase == iPhoneTouchPhase.Moved &&
		    touch2.phase == iPhoneTouchPhase.Moved) {
  	
			curDist = touch.position - touch2.position; 

			prevDist = (touch.position - touch.deltaPosition) - 
			                  (touch2.position - touch2.deltaPosition); 
		
			slide = moveFactor * (prevDist.magnitude - curDist.magnitude);
			
			mag = transform.position.magnitude;
			slide = Mathf.Clamp(mag + slide, minDist, maxDist);
			dVec = Vector3.forward * (mag - slide); 		
 		
			pinch = true;
		}
	}  
}

function Update () {
	//
	// process all frame updates here
	//
	
	// show next device move
	if (null != nextDeviceMove) {
		PlayerMove(nextDeviceMove.transform.position);
		Destroy(nextDeviceMove);
		nextDeviceMove = null;
	}
		
	if (undo) {
		undo = false;
		LastMoveUndo();
	} 

	if (popup)
		return;

	//
	// Do 3D dtuff here
	//
	
	if (swipe != 0) {
		// rotate around the origin along the selected major axis (dVec)
		transform.RotateAround (Vector3.zero, dVec, swipe * rotationRate * Time.deltaTime);
		//swipe = swipe - k*i;
		
		// As coded we get a continuos rotation if the swipe has not ended,
		// even when the touch is held stationary.
		//
		// Uncomment the line below to stop rotation when touch is 
		// stationary but not ended

		// swipe = 0;
	} else if (pinch) {
		// move the camera in and out based on how far we pinched
		transform.Translate(dVec); 
		
		// make sure we're still looking at the origin
		transform.LookAt(Vector3.zero);
		
		// don't pinch on next update
		pinch = false;
	}
	
	//
	// Check to see if the user selected a MoveDot
	//
  
	// don't process taps while we're in the undo time interval
	if (!tap || undoAllowed)
		return;	
		
	tap = false;

	if (iPhoneInUse) {
		myPosition = touch.position;
	}
  
	// We need to actually tap on an object
	if (!Physics.Raycast(Camera.main.ScreenPointToRay(myPosition),  hit, 100))
		return;
		
	// And we need to hit a rigidbody that is not kinematic
	if (!hit.rigidbody || hit.rigidbody.isKinematic)
		return;

	go = hit.rigidbody.gameObject;
	
	// get position of move dot that was tapped
	pos = go.transform.position;

	// destroy move dot that was tapped
	Destroy(go);	
  
	undoAllowed = PlayerMove(pos);  
}

function PlayerMove(pos: Vector3) {
	// place player token in gameboard
	
	return true;
}

function DeviceMove() {
	// logic for next device move
	
	// nextDeviceMove = Game Object of selected move dot	
}

function LastMoveUndo() {
	// remove player token
	
	// restore move dot
	
	undo = false;
}
Categories
iOS Development Programming

iPhone Utility App with EAGLView on Flipside

I am just getting started on OpenGL ES development for the iPhone. There’s a lot of sample code out there, but it’s mostly basic stuff. This post presents (hopefully) a slightly more useful example.

I started with the existing instructions found here. I will not walk you through creating the basic template (OpenGL ES Application and Utility Application) applications in XCode–if you can’t do at least that much on your own, then it’s probably best you learn how to do that much and come back later! The following was done in XCode 3.2.2.

I will repeat the basic steps from the link above and highlight my changes, as such.

  1. Use the utility app as a base
  2. Add QuartzCore and OpenGLES frameworks
  3. Copy EAGLView files (*Render*, EAGLView*) across from your OpenGL template app (these last two steps are easily accomplished having both template application projects open in XCode at the same time and dragging from one project to the other.)
  4. In the FlipsideView.xib file change View to be type EAGLView
  5. In FlipsideViewController add “@class EAGLView” and an EAGLView ivar called glView and make it an IBOutlet property, so it looks like this:
  6. //
    //  FlipsideViewController.h
    //
    
    #import 
    
    @class EAGLView;
    
    @protocol FlipsideViewControllerDelegate;
    
    @interface FlipsideViewController : UIViewController {
    
        id  delegate;
        EAGLView *glView;
    
    }
    
    @property (nonatomic, assign)
            id  delegate;
    @property (nonatomic, retain)
            IBOutlet EAGLView *glView;
    - (IBAction)done;
    
    @end
    
    @protocol FlipsideViewControllerDelegate
    - (void)flipsideViewControllerDidFinish:
            (FlipsideViewController *)controller;
    @end
  7. In IB FlipsideView.xib connect from File’s Owner to the new glView.  At this point if you save all files in IB and invoke build and run (ignoring the @synthesize warning,) you have the basic functionality.  Running in the simulator you should see this:

    When you click the info button the flipside will appear and you should see this:

    Note that we have a static image here. The code to animate the colored box is shown in the next step.
  8. Make changes to FlipsideViewController.m methods so it looks like this:
  9. //
    //  FlipsideViewController.m
    //  util
    //
    
    #import "FlipsideViewController.h"
    #import "EAGLView.h"
    
    @implementation FlipsideViewController
    
    @synthesize delegate;
    @synthesize glView;
    
    - (void)viewDidLoad {
    
        [super viewDidLoad];
        self.view.backgroundColor =
             [UIColor viewFlipsideBackgroundColor];
    
        self.glView.animationFrameInterval = 1.0 / 60.0;
        [self.glView startAnimation];
    }
    
    - (IBAction)done {
        self.glView.animationFrameInterval = 1.0 / 5.0;
        [self.glView stopAnimation];
        
        [self.delegate flipsideViewControllerDidFinish:self];
    }
    
    - (void)didReceiveMemoryWarning {
        // Releases the view if it doesn't have a superview.
        [super didReceiveMemoryWarning];
        // Release any cached data, images, etc that aren't in use.
    }
    
    - (void)viewDidUnload {
        // Release any retained subviews of the main view.
        // e.g. self.myOutlet = nil;
    }
    
    - (void)dealloc {
        [super dealloc];
    }
    
    @end
    

At this point, build and debug, then hit the info button–you should have a bouncing box in the flip side! (Note that I’ve added the “@synthesize glView;” as I should have earlier.)

Most of the games I’ve seen present some GUI elements first to select number of players, level, etc., prior to the actual game play. I think this example presents a more realistic template for implementing that use case; selecting number of players and such can be done on the main view then a button push invokes the flip side view for game play. Good luck with your development!