//Copyright (c) 2004 Aaron Sullivan. All rights reserved.
#ifndef SB_GAMEPHASE_H
#define SB_GAMEPHASE_H
#include "sb_Game.h"
#include "sb_global.h"
class sb_GamePhase
{
public:
sb_GamePhase( sb_GamePtr gameToUse );
virtual ~sb_GamePhase() = 0;
virtual void start() = 0;
virtual void pause() = 0;
virtual void end() = 0;
virtual void reset() = 0;
virtual void processEvent( SDL_Event &event ) = 0;
virtual void animate() = 0;
virtual void draw() = 0;
int isDone();
protected:
int done;
sb_GamePtr game;
};
typedef sb_GamePhase* sb_GamePhasePtr;
#endif
//Copyright (c) 2004 Aaron Sullivan. All rights reserved.
#include "sb_Phase_Battle.h"
#include "iostream.h"
#include "sb_Missile.h"
sb_Phase_Battle::sb_Phase_Battle( sb_GamePtr gameToUse)
: sb_GamePhase( gameToUse ) //call sb_GamePhase constructor
{
game = gameToUse;
reset();
}
sb_Phase_Battle::~sb_Phase_Battle()
{
}
void sb_Phase_Battle::start()
{
done = false;
time = 0;
secondsLeft = totalSeconds;
//title variables
titleX = 12.0f, titleY = 745.0f;
titleAlpha = 0.8f;
titleScaleX = 10.0f;
titleScaleY = 2.0f;
titleGlow = 0.0f;
//phase specific
battling = false;
waitingForMissiles = false;
game->musicBox.play(kMusic_battle);
//start keeping track of Ticks passed
game->updateTicksPassed();
}
void sb_Phase_Battle::pause()
{
game->musicBox.pauseToggle();
}
void sb_Phase_Battle::end()
{
done = true;
}
void sb_Phase_Battle::reset()
{
totalSeconds = 15;
//start();
}
void sb_Phase_Battle::processEvent( SDL_Event &event )
{
switch (event.type)
{
//pointer position comes from game object ( which has its own processEvent function )
case SDL_MOUSEBUTTONDOWN:
if (battling)
{
int i;
for (i=0; i <= game->indexOfLastGun; i++)
{
if (game->gun[i].isActive() && game->gun[i].isReloading() == false)
{
if (game->playerMissiles.launch(game->gun[i].getX(), game->gun[i].getY(),
game->gun[i].getRotation(),
game->pointerX, game->pointerY, 0))
{
game->noisemaker.play(kFX_playerMissile);
game->gun[i].reload(); //start the delayed reload
break;
}
}
}
if (i > game->indexOfLastGun) //if there were no guns available to fire... or no missiles available
{
game->noisemaker.play(kFX_click); //audio que for failed shot
}
}
/* uncomment if you want opening animation to be skipable
else if(time > 200)
{
battling = true;
timeBattleStarted = time;
bargesShowing = 5;
}
*/
break;
case SDL_MOUSEBUTTONUP:
break;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_SPACE)
{
//skip animated intro?
}
//'F' fades out music
if (event.key.keysym.sym == SDLK_f)
{
game->musicBox.fadeOut();
}
//'M' toggles music on and off
if (event.key.keysym.sym==SDLK_m)
{
if(game->musicBox.isPlaying())
{
game->musicBox.stop();
}
else
{
game->musicBox.play();
}
}
break;
case SDL_KEYUP:
break;
default:
break;
}
}
void sb_Phase_Battle::animate()
{
if (done) return; //necessary so that last gun placed does not move
int i;
Uint32 ticksPassed;
game->updateTicksPassed();
ticksPassed = game->getTicksPassed();
//Time based events
time = time + ticksPassed;
Uint32 startTime, endTime;
float transitionTime; //will hold amount
for (i=0; i <= game->indexOfLastGun; i++)
{
game->gun[i].animate( ticksPassed );
}
game->playerMissiles.animate( ticksPassed );
if (game->playerMissiles.explodeSound())
{
game->noisemaker.play(kFX_missileBlast);
}
if (time > 500 && battling == false && waitingForMissiles == false)
{
battling = true;
game->destroyers.go();
timeBattleStarted = 500;
}
if (battling)
{
//animate the title of the phase
//"stretch in"
startTime = timeBattleStarted; endTime = timeBattleStarted+500; transitionTime = (float)(endTime - startTime - 1);
if (time > startTime && time < endTime)
{
//the total amount to change (over whole time) is divided total time of transition and multiplied by how many ticks passed
//then that portion (which is the fraction needed for THIS update) is subtracted from the current value
titleScaleX -= 9.0f/transitionTime*ticksPassed;
}
if (time > endTime) titleScaleX = 1.0f; //take care of inaccuracy of method
//glow the title
startTime = timeBattleStarted+500; endTime = timeBattleStarted+1000; transitionTime = (float)(endTime - startTime - 1);
if (time > startTime && time < endTime)
{
if (titleGlow == 0.0f)
{
titleGlow = 1.0f;
}
titleGlow -= 1.0/transitionTime*ticksPassed;
}
if (time > endTime) titleGlow = 0.0f;
//raise and vanish title
startTime = timeBattleStarted+2500; endTime = timeBattleStarted+3000; transitionTime = (float)(endTime - startTime - 1);
if (time > startTime && time < endTime)
{
titleY -= 100.0f/transitionTime*ticksPassed;
titleAlpha -= 0.8f/transitionTime*ticksPassed;
if (titleAlpha < 0.0f) titleAlpha = 0.0f;
titleScaleY += 10.0f/transitionTime*ticksPassed;
}
if (time >= endTime) {
titleY = 0.0;
titleAlpha = 0.0f;
titleScaleY = 4.0f;
}
//timer
//*this phase is ended when the timer (secondsLeft) runs out
secondsLeft = totalSeconds - (time-timeBattleStarted)/1000; //time/1000 turns milliseconds into seconds
if (secondsLeft < 0)
{
secondsLeft = 0;
battling = false;
timeBattleEnded = time;
waitingForMissiles = true;
game->destroyers.stop();
game->musicBox.fadeOut();
}
//destroyers
game->destroyers.animate( ticksPassed );
}
sb_Circle dCircle;
int dIndex;
dCircle = game->destroyers.getNextCollisionCircle(dIndex); //dIndex is updated
while (dIndex > -1)
{
//if any player missiles are exploding on this destroyer damage it
if (game->playerMissiles.anyCollisionWith(dCircle))
{
game->destroyers.damage(dIndex);
game->noisemaker.play(kFX_missileHit);
}
//if this destroyer is over a vacant spot, it means they have come into close range of
//the barges and the game is over
if (game->grid.getSectorValue((int)(dCircle.getX() / game->grid.getSectorSize()), (int)(dCircle.getY() / game->grid.getSectorSize()))
!= kST_outOfRange)
{
game->gameover = true;
end();
}
dCircle = game->destroyers.getNextCollisionCircle(dIndex); //dIndex is updated
}
//if all the destroyers are gone, end wave
if (battling && game->destroyers.allInactive())
{
secondsLeft = 0;
battling = false;
timeBattleEnded = time;
waitingForMissiles = true;
game->destroyers.stop();
game->musicBox.fadeOut();
}
//we wait for the missiles to finish their path
if (waitingForMissiles)
{
game->destroyers.animate( ticksPassed );
if (game->playerMissiles.allInactive() && game->destroyers.isStopped())
{
waitingForMissiles = false;
//if we are on or past the third stage there are no more new enemy reinforcements... so we'll move on to the next stage
if (game->destroyers.allInactive() && game->currentWave >= 3)
{
game->newWave = true; //let the newWave phase take care of advancing us to the next Stage
waitingForMissiles = true;
}
//on to the next wave
end();
}
}
}
void sb_Phase_Battle::draw()
{
glMatrixMode(GL_MODELVIEW); //we select the Model matrix to do our drawing
glBindTexture(GL_TEXTURE_2D, glAA_texture[0]);
game->grid.displayShields();
int i;
for (i=0; i < game->totalBarges; i++)
{
game->barge[i].draw();
}
for (i=0; i <= game->indexOfLastGun; i++)
{
game->gun[i].draw();
}
game->playerMissiles.draw();
if (battling)
{
drawTitle();
drawTimer();
drawPointer();
}
game->destroyers.draw();
}
void sb_Phase_Battle::drawTimer()
{
glAAFlush();
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, glAA_texture[0]);
vf_setpenscale(3.5f, 3.5f);
vf_setpenloc( 10.0f, 690.0f );
glAALineWidth(3);
glAAColor4f(1.0f, 1.0f, 1.0f, 0.5f);
if (secondsLeft >= 10)
{
vecString("%d", secondsLeft);
}
else
{
vecString("0%d", secondsLeft);
}
}
void sb_Phase_Battle::drawTitle()
{
vf_setpentrack(8.0f, 0.0f);
if (titleGlow > 0.0f)
{
glAAFlush();
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, glAA_texture[2]);
vf_setpenscale(titleScaleX, titleScaleY);
vf_setpenloc( titleX, titleY );
glAALineWidth(10);
glAAColor4f(1.0f, 1.0f, 1.0f, titleGlow);
vecString("OPEN FIRE!");
}
glAAFlush();
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D, glAA_texture[0]);
vf_setpenscale(titleScaleX, titleScaleY);
vf_setpenloc( titleX, titleY );
glAALineWidth(3);
glAAColor4f(1.0f, 1.0f, 1.0f, titleAlpha);
vecString("OPEN FIRE!");
}
void sb_Phase_Battle::drawPointer()
{
glAAFlush();
glLoadIdentity();
glTranslatef(game->pointerX, game->pointerY, 0.0f);
glAALineWidth(2);
glAAColor4f(1.0f, 1.0f, 0.0f, 0.6f);
glAABegin(GL_LINE_STRIP);
glAAVertex2f(0.0f,0.0f);
glAAVertex2f(0.0f,15.0f);
glAAVertex2f(4.0f,10.0f);
glAAVertex2f(12.0f,12.0f);
glAAVertex2f(0.0f,0.0f);
glAAEnd();
glAAColor4f(1.0f, 1.0f, 0.0f, 0.2f);
}
#ifndef SN_PUSHABLEBLOCKCOLLECTION
#define SN_PUSHABLEBLOCKCOLLECTION
#include "sn_PushableBlock.h"
#include <string>
using namespace std;
class sn_PushableBlockCollection
{
public:
void init( sn_3DmodelCollection * p3DModelCollection, sn_MovementArray * pMA, vector<sn_StageElement*> * pSE );
void clear();
int load(const string &filename);
int save(const string &filename);
sn_PushableBlock* addNew( float x, float y, float z, int blockType);
void updateMovementDeltas();
bool eraseArea( float x, float y, float z);
void animate( unsigned int ticksPassed );
void dropTest(); //test for new situation in which blocks should fall
vector<sn_PushableBlock*>::iterator findCollidingBlock( sn_Collider &c);
vector<sn_PushableBlock*>::iterator findCollidingBlock( vector<sn_PushableBlock*>::iterator &pBlock);
vector<sn_PushableBlock*>::iterator getBeginItr();
vector<sn_PushableBlock*>::iterator getEndItr();
private:
vector<sn_PushableBlock*> blocks;
sn_3DmodelCollection * pModels;
sn_MovementArray * pMovementArray;
vector<sn_StageElement*> * hStageElements;
vector<string>modelFilenames;
vector<string>movementMapFilenames;
};
#endif
//
// ST_Button_StageSlider.m
// SnowballTouch
//
// Created by Aaron Sullivan on 8/30/08.
// Copyright 2009 Buzzabit Entertainment. All rights reserved.
//
#import "ST_Button_StageSlider.h"
#import "ST_GameManager.h"
#import "ST_Prefs.h"
#define kActualTrackDistance 400
#define kStageNumYOffset 0
@implementation ST_Button_StageSlider
@synthesize stageMax, stageStart, gameNumber, x, y, xOrigin, yOrigin, pressed, state;
-(id)initWithStageManager:(ST_StageManager*)sm Game:(int)g X:(float)ix Y:(float)iy {
self = [super init];
if (self != nil) {
gameNumber = g;
stageManager = sm;
stageStart = stageMax = [ST_Prefs getStageForGame:gameNumber];
x = ix;
y = iy;
xOrigin = ix;
yOrigin = iy;
trackStartY = iy - kActualTrackDistance/2;
trackDistPerStage = kActualTrackDistance / stageManager.totalStages;
trackStageMaxY = trackStartY + trackDistPerStage * stageMax;
const float trackOffset = 8.0;
const float trackBossStageY = trackStartY + trackDistPerStage * stageManager.bossStage;
label_slider = [[ST_Label alloc] initWithText:@"" Rect:CGRectMake(20, 20, 90, 440)];
[label_slider show];
button_track = [[ST_Button alloc] initWithTexture:@"GUI_stageProgressBar_396x16.png" X:ix-trackOffset Y:iy-10.0 Width:16 Height:kActualTrackDistance + 60.0];
button_player = [[ST_Button alloc] initWithTexture:@"GUI_playerIcon_128x128.png" X:ix+trackOffset+5 Y:trackStageMaxY Width:64 Height:64 ];
snmX = ix + 42;
snmY = iy + kStageNumYOffset;
button_triangle = [[ST_Button alloc] initWithTexture:@"GUI_triangle_64x64.png" X:ix-trackOffset-3 Y:trackStageMaxY Width:64 Height:64 ];
button_x = [[ST_Button alloc] initWithTexture:@"GUI_goalX_64x64.png" X:ix-trackOffset Y:trackBossStageY Width:64 Height:64 ];
[button_track setAlpha:1.0];
[button_player setAlpha:1.0];
[button_triangle setAlpha:1.0];
[button_x setAlpha:1.0];
prevPoint.x = xOrigin;
prevPoint.y = yOrigin;
pressed = NO;
stageNumModel = [[ST_Model alloc] initAsSquareWithAnimatedTexture:@"texture_num.png" withFrameBoundsX:5 Y:2];
//set up details label with info on current stage
label_details = nil;
[self initDetailLabel];
positionAnimator = [[ST_AnimatorQ3 alloc] init];
}
return self;
}
-(void)dealloc {
[button_track release];
[button_player release];
[button_triangle release];
[button_x release];
[label_slider release];
[label_details release];
[positionAnimator release];
[stageNumModel release];
[super dealloc];
}
-(BOOL)pressAttemptAtX:(int)px Y:(int)py {
if ([button_player pressAttemptAtX:px Y:py] == YES) {
state = kst_button_stageSlider_state_playerDrag;
pressed = YES;
return YES;
}
else {
pressed = NO;
return NO;
}
}
-(BOOL)dragAttemptAtX:(float)dx Y:(float)dy {
if (state == kst_button_stageSlider_state_playerDrag) {
float newY = dy;
if (newY > trackStageMaxY)
newY = trackStageMaxY;
else if (newY < trackStartY)
newY = trackStartY;
[button_player forcePositionX:button_player.x Y:newY];
stageStart = (button_player.y - trackStartY)/trackDistPerStage;
pressed = YES;
return YES;
}
else {
pressed = NO;
return NO;
}
}
-(void)letGo {
pressed = NO;
if (state == kst_button_stageSlider_state_playerDrag) {
//figure out stage based on new position of playerIcon
stageStart = (button_player.y - trackStartY)/trackDistPerStage;
//snap the position of the icon to the area where that stage should be represented
[button_player letGo];
[button_player forcePositionX:button_player.x Y:trackStartY + stageStart*trackDistPerStage];
state = kst_button_stageSlider_state_still;
//set up details button
[self initDetailLabel];
}
}
-(void)draw {
[label_slider draw];
//draw the whole layout
[label_details draw];
[button_track draw];
[button_x draw];
[button_triangle draw];
[button_player draw];
//draw the stage number over the player button
glPushMatrix();
int frame1 = stageStart % 10;
glPushMatrix();
[ST_Model guiTransformX:snmX Y:button_player.y-13.0 W:32.0 H:16.0];
[stageNumModel setTextureFrame:frame1];
[stageNumModel drawAlpha:YES ReverseX:NO Y:YES DepthTest:NO];
glPopMatrix();
if (stageStart >= 10) {
int frame10 = stageStart / 10;
glPushMatrix();
[ST_Model guiTransformX:snmX Y:button_player.y-26.0 W:32.0 H:16.0];
[stageNumModel setTextureFrame:frame10];
[stageNumModel drawAlpha:YES ReverseX:NO Y:YES DepthTest:NO];
glPopMatrix();
}
glPopMatrix();
}
-(void)animate {
[button_player animate];
[label_details animate];
[label_slider animate];
}
-(void)updateButtonPositions {
trackStartY = yOrigin - kActualTrackDistance/2;
trackStageMaxY = trackStartY + trackDistPerStage * stageMax;
const float trackStageStartY = trackStartY + trackDistPerStage * stageStart;
const float trackOffset = 8.0;
[button_player forcePositionX:xOrigin+trackOffset+5.0 Y:trackStageStartY];
[button_triangle forcePositionX:xOrigin-trackOffset-3.0 Y:trackStageMaxY];
}
-(void)initDetailLabel {
if (label_details != nil) {
[label_details release];
}
//get info from current selected stage (stageStart)
gameNumber = [ST_GameManager instance].currentGameFile;
int bestTime = [ST_Prefs getTimeForStage:stageStart
InGame:gameNumber];
int totalStageTime = [ST_Prefs getTotalTimeForStage:stageStart
InGame:gameNumber];
int bestSteps = [ST_Prefs getStepsForStage:stageStart
InGame:gameNumber];
int bHours = bestTime / 3600;
int bMinutes = bestTime / 60 - bHours * 60;
int bSeconds = bestTime - (bHours * 3600 + bMinutes * 60);
int tHours = totalStageTime / 3600;
int tMinutes = totalStageTime / 60 - tHours * 60;
int tSeconds = totalStageTime - (tHours * 3600 + tMinutes * 60);
label_details = [[ST_Label alloc] initWithText:[NSString stringWithFormat:
@"Stage %d\n%@\n\n Best Steps: %d\n Best Time: %02d:%02d:%02d\n Total Time: %02d:%02d:%02d"
, stageStart
, [stageManager getStageNameForStageNumber:stageStart]
, bHours, bMinutes, bSeconds
, bestSteps
, tHours, tMinutes, tSeconds]
Rect:CGRectMake(140.0, 20.0, 160, 320)];
[label_details show];
}
@end
Though largely feature complete, I couldn't keep up with the rapidly changing platform alone so it was never released. Even so, I found tremendous reward in exploring all the possibilities of the curious multitouch screen and accelerometer. (video)
I finally released an iPhone game on the App Store at the end of 2013 by collaborating with a partner and using Unity. Below is a C# script I wrote (several times) that implements the ambitious control scheme for the game.
/*
* InputSB
* Attach to a gameObject to expose properties
* A layer named InterfaceSurface is needed for this to work
* Usage:
*
* Check for state using static methods GetButtonDown(0), GetButtonDown(1), GetDragging()
* After confifming state you can check public state variables (all static):
* pointerPosition (like mousePosition)
* surfacePointerPosition (position of pointer on interface surface)
* dragDelta (change in position since last update while dragging)
* surfaceDragDelta (like dragDelta but on a 3D surface which is great for "map dragging")
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public enum InputState
{
mainGame,
none
}
public enum ButtonIDs
{
down1,
down2,
endTimer,
pause,
camera
}
public class InputSB : MonoSingleton<InputSB> {
public Vector2 fingerOffset = new Vector2(0.35f,0.15f); //fraction of an inch
public Camera interfaceCamera = null;
public GameObject interfaceSurface = null;
public List<Rect> guiRectList = new List<Rect>(); //use to add a mask so we can ignore some clicks
public Vector3 AxisUsedOn2DPlane = new Vector3(1f,1f,0f); // put a 1 in 2 axis and a 0 in the other
public float accelerateMultiplier = 0.5f;
//public state variables
public static Vector2 pointerPosition = new Vector2(-1f,-1f);
public static Vector2 rawPosition = new Vector2(-1f,-1f);
public static Vector2 dragDelta = new Vector2(0f,0f);
public static Vector3 surfaceClickPosition = new Vector3(-10001f, -10001f, -10001f);
public static Vector3 surfaceDragDelta = new Vector3(0f,0f,0f);
public InputState inputState = InputState.mainGame;
public Vector3 cornerBottomLeft;
public Vector3 cornerTopLeft;
public Vector3 cornerTopRight;
public Vector3 cornerBottomRight;
//internal tracking
private int firstTouchID = -1;
private int secondTouchID = -1;
private static bool downTouch = false;
private static bool upTouch = false;
private static bool down1 = false;
private static bool down2 = false;
private static bool pause = false;
private static bool endTimer = false;
private static bool camera = false;
private Vector2 prevPosition;
public bool left = false;
private Vector3 surfacePrevClickPosition;
private int interfaceSurfaceLayer;
private delegate void InputStateDelegate();
private InputStateDelegate currentStateDelegate;
private bool gamePaused = false;
// Use this for initialization
public override void Init ()
{
//if no camera is provided we use the main
if (interfaceCamera == null) {
interfaceCamera = Camera.main;
}
//if no drag surface is supplied, create one
//A layer named InterfaceSurface is needed for this to work
interfaceSurfaceLayer = LayerMask.NameToLayer("InterfaceSurface");
//if the layer exists we can create the surface or set the provided one to the correct layer
if (interfaceSurfaceLayer != -1) {
if (interfaceSurface == null) {
interfaceSurface = new GameObject("InterfaceSurface");
interfaceSurface.AddComponent<BoxCollider>();
interfaceSurface.transform.parent = gameObject.transform;
interfaceSurface.transform.localScale = new Vector3(AxisUsedOn2DPlane.x * 10000f,AxisUsedOn2DPlane.y * 10000f,AxisUsedOn2DPlane.z * 10000f);
interfaceSurface.layer = interfaceSurfaceLayer;
}
else {
interfaceSurface.layer = interfaceSurfaceLayer;
}
}
else {
print ("InputSB: A Layer named InterfaceSurface is needed for InputCX to function");
}
cornerBottomLeft = getSurfacePosition(new Vector2(0f,0f));
// print ("bottom left: " + cornerBottomLeft);
cornerTopLeft = getSurfacePosition (new Vector2(0f,Screen.height));
// print ("top left: " + cornerTopLeft);
cornerTopRight = getSurfacePosition(new Vector2(Screen.width, Screen.height));
// print ("top right: " + cornerTopRight);
cornerBottomRight = getSurfacePosition (new Vector2(Screen.width, 0f));
// print ("bottom right: " + cornerBottomRight);
//FinishClick(); //resets all the values including setting our state delegate
currentStateDelegate = MainGame;
inputState = InputState.mainGame;
}
void OnEnable() {
MenuManager.OnGamePause += OnGamePause;
MenuManager.OnGameUnPause += OnGameUnPause;
}
void OnDisable() {
MenuManager.OnGamePause -= OnGamePause;
MenuManager.OnGameUnPause -= OnGameUnPause;
}
public void OnLevelWasLoaded(){
if (interfaceCamera == null) {
interfaceCamera = Camera.main;
}
cornerBottomLeft = getSurfacePosition(new Vector2(0f,0f));
// print ("bottom left: " + cornerBottomLeft);
cornerTopLeft = getSurfacePosition (new Vector2(0f,Screen.height));
// print ("top left: " + cornerTopLeft);
cornerTopRight = getSurfacePosition(new Vector2(Screen.width, Screen.height));
// print ("top right: " + cornerTopRight);
cornerBottomRight = getSurfacePosition (new Vector2(Screen.width, 0f));
// print ("bottom right: " + cornerBottomRight);
}
public GameObject GetInterfaceSurface() {
return interfaceSurface;
}
// Update is called once per frame
void Update ()
{
currentStateDelegate();
}
private void registerButtonDown(Touch t) {
// if we're paused, the only second touch we care about is unpausing
if((Application.loadedLevel == (int)Levels.Battle || Application.loadedLevel == (int)Levels.Tutorial) && MenuManager.instance.paused){
if(TouchButtonsManager.instance.WithinPauseButton(getSurfacePosition(t.position)))
pause = true;
return;
}
if (TouchButtonsManager.instance.WithinPrimaryButton(getSurfacePosition(t.position))){// t.position.y < Screen.height * 0.5) {
down1 = true;
}
else if(TouchButtonsManager.instance.WithinSecondaryButton(getSurfacePosition(t.position))){
down2 = true;
}
else if(TouchButtonsManager.instance.WithinPauseButton(getSurfacePosition(t.position))){
pause = true;
}
else if(TouchButtonsManager.instance.WithinEndTimerButton(getSurfacePosition(t.position))){
endTimer = true;
}
else if(TouchButtonsManager.instance.WithinCameraButton(getSurfacePosition(t.position))){
camera = true;
}
}
public static bool GetStartTouching() {
return downTouch;
}
public static bool GetEndTouching() {
return upTouch;
}
public static bool GetDragging() {
if (dragDelta.x == 0f && dragDelta.y == 0f) return false;
else return true;
}
public static float GetDragAmount(){
if(!GetDragging()) return 0.0f;
return dragDelta.magnitude;
}
public static bool GetButtonDown( int button ) {
if (button == (int)ButtonIDs.down1 && down1) {return true;}
else if (button == (int)ButtonIDs.down2 && down2) {return true;}
else if (button == (int)ButtonIDs.endTimer && endTimer) {return true;}
else if (button == (int)ButtonIDs.pause && pause) {return true;}
else if (button == (int)ButtonIDs.camera && camera) {return true;}
else return false;
}
public bool GetTouching(){
return firstTouchID != -1;
}
//Input states:
//MainGame
private void MainGame() {
downTouch = false; // if there was a down event we erase it now
down1 = false;
down2 = false;
pause = false;
endTimer = false;
camera = false;
upTouch = false;
dragDelta = new Vector2(0f,0f);
//up1 = false;
//up2 = false;
bool t1Held = false;
bool t2Held = false;
surfaceDragDelta = surfaceClickPosition - surfacePrevClickPosition;
//if game is paused let regular gui inputs work only (for options menu)
if (gamePaused) return;
Touch t1 = new Touch();
Touch t2 = new Touch();
foreach (Touch t in Input.touches) {
if (t.fingerId == firstTouchID) {
t1 = t;
t1Held = true;
}
if (t.fingerId == secondTouchID) {
t2 = t;
t2Held = true;
}
}
//if we already have a touchID for the first touch
if (t1Held) {
if (t1.phase == TouchPhase.Moved) {
setPointerWithOffset (t1.position);
dragDelta = t1.deltaPosition;
SetSurfaceClickPosition();
}
else if (t1.phase == TouchPhase.Ended || t1.phase == TouchPhase.Canceled) {
firstTouchID = -1;
dragDelta = new Vector2(0f,0f);
upTouch = true;
}
}
//if we already have a touchID for the second touch
if (t2Held) {
if (t2.phase == TouchPhase.Ended || t2.phase == TouchPhase.Canceled) {
secondTouchID = -1;
}
}
int count = Input.touchCount;
//if touches
if (count == 1) {
Touch t = Input.GetTouch(0);
if (t.phase == TouchPhase.Began) {
registerDownTouch(t.position);
firstTouchID = t.fingerId;
setPointerWithOffset(t.position);
SetSurfaceClickPosition();
}
}
else if (count == 2) {
Touch touch1 = Input.GetTouch(0);
Touch touch2 = Input.GetTouch(1);
if (touch1.phase == TouchPhase.Began
&& touch2.phase == TouchPhase.Began) {
//both of these came down at once
//we'll just choose 1 to be the pointer
registerDownTouch(touch1.position);
firstTouchID = touch1.fingerId;
setPointerWithOffset(touch1.position);
SetSurfaceClickPosition();
secondTouchID = touch2.fingerId;
registerButtonDown(touch2);
}
else {
if (t1Held) {
foreach (Touch t in Input.touches) {
if (t.fingerId != firstTouchID && t.phase == TouchPhase.Began) {
registerButtonDown(t);
if (secondTouchID == -1) secondTouchID = t.fingerId;
}
}
}
}
}
else if (count > 2) {
//if we are already holding down our first finger
//we can register any other touches
if (t1Held) {
foreach (Touch t in Input.touches) {
if (t.fingerId != firstTouchID && t.phase == TouchPhase.Began) {
registerButtonDown(t);
if (secondTouchID == -1) secondTouchID = t.fingerId;
}
}
}
}
//deal with possible mouse input
else {
if (Input.GetAxis("Mouse X") != 0.0 || Input.GetAxis ("Mouse Y") != 0.0 ) {
prevPosition = pointerPosition;
pointerPosition = Input.mousePosition;
rawPosition = pointerPosition;
SetSurfaceClickPosition();
}
if (Input.GetMouseButtonDown ((int)ButtonIDs.down1)) {
down1 = true;
prevPosition = pointerPosition;
pointerPosition = Input.mousePosition;
rawPosition = pointerPosition;
}
else if (Input.GetMouseButtonDown((int)ButtonIDs.down2)){
down2 = true;
prevPosition = pointerPosition;
pointerPosition = Input.mousePosition;
rawPosition = pointerPosition;
}
else if(Input.GetMouseButtonDown((int)ButtonIDs.endTimer)){
endTimer = true;
prevPosition = pointerPosition;
pointerPosition = Input.mousePosition;
rawPosition = pointerPosition;
}
}
}
//Input State: None
private void None() {
}
private void registerDownTouch(Vector2 screenPos) {
downTouch = true;
left = (screenPos.x < Screen.width * 0.5);
}
private void setPointerWithOffset(Vector2 screenPos) {
prevPosition = pointerPosition;
pointerPosition = screenPos;
rawPosition = pointerPosition;
if(Application.loadedLevel == (int)Levels.Battle || Application.loadedLevel == (int)Levels.Tutorial){
//offset depending on how far across screen we are
//float percentAcross = 1f - screenPos.x / Screen.width;
//float newX = screenPos.x + (fingerOffset.x * 2f * percentAcross - fingerOffset.x);
float offsetX = fingerOffset.x * Screen.dpi;
float offsetY = fingerOffset.y * Screen.dpi;
float newX, newY;
if (PlayerDataTracker.instance.fastPointOn) {
//accelerated
//if we start from the right we subtract the amount of "accelerated" x
if (!left) {
newX = (screenPos.x - ((Screen.width - screenPos.x) * accelerateMultiplier)) - offsetX;
}
else {
newX = (screenPos.x + (screenPos.x * accelerateMultiplier)) + offsetX;
}
newY = (screenPos.y + (screenPos.y * accelerateMultiplier)) + offsetY;
}
else {
if (left) {
newX = screenPos.x + offsetX;
}
else {
newX = screenPos.x - offsetX;
}
newY = screenPos.y + offsetY;
}
pointerPosition = new Vector2(newX, newY);
}
}
//SetSurfaceClickPosition()
//clickPosition needs to be set immediately before calling this
//it casts onto a surface in order to get an accurate drag distance on a surface
private void SetSurfaceClickPosition() {
if(interfaceCamera == null)
interfaceCamera = Camera.main;
if (interfaceSurface) {
RaycastHit hit;
int layerMask = 1 << interfaceSurfaceLayer; //bitshift in layer number to raycast against it only
Physics.Raycast(interfaceCamera.ScreenPointToRay(pointerPosition), out hit, 10000.0f, layerMask);
if (hit.transform != null) {
surfaceClickPosition = hit.point;
}
//we have to calculate the previous point on the geometry from the prev 2D point
//because the ground has shifted under the camera and if we kept using the 3D point of the surface
//it would immediately delta right back to where we were in a stutter (proven)
Physics.Raycast(interfaceCamera.ScreenPointToRay(prevPosition), out hit, 10000.0f, layerMask);
if (hit.transform != null) {
surfacePrevClickPosition = hit.point;
}
}
}
public Vector3 getSurfacePosition(Vector2 screenPosition) {
if(interfaceCamera == null)
interfaceCamera = Camera.main;
if (interfaceSurface) {
RaycastHit hit;
int layerMask = 1 << interfaceSurfaceLayer; //bitshift in layer number to raycast against it only
Physics.Raycast(interfaceCamera.ScreenPointToRay(screenPosition), out hit, 10000.0f, layerMask);
if (hit.transform != null) {
return hit.point;
}
else return new Vector3(0f,0f,0f);
}
else return new Vector3(0f,0f,0f);
}
//update internal status when game pause event occurs
public void OnGamePause() {
gamePaused = true;
}
public void OnGameUnPause() {
gamePaused = false;
}
}