Flicking, Shooting, Throwing, Tossing, Lobbing, Slicing script

Hi everyone,

We’ve recently completed our first mini game using Unity for iOS. It was a basketball (think papertoss) like game where you had to throw the ball in certain buckets and score points.

I remember when I was first getting started I had a bit of a hard time finding a way to flick stuff around.
I’m a technical artist but even javascript was new to me, most of the technical things I do are in the various software programs we use.

Anyway, I thought I’d share what ended up working for us. This script was adapted from others I found posted around here, so I’m not fully taking credit for any or all of this. I went through a variety of different versions and this one seemed to work best.

Basically it reads a finger swipe, and instantiates and adds a force to a game object based on that finger swipe. It takes into account the amount of time you’ve had your finger on screen, the speed of the flick, and the direction of the flick. This is then translated into 3D terms when adding the force.

In addition to sharing this, I’m really open to any stupid things I did in the code, ways to do it differently, etc., because I’m sure there are lots of newbie mistakes.

Anyway, hopefully someone can find this useful. If it wasn’t for the big community behind Unity I’d probably still be pulling my hair out trying to get something to work.

Adios!

#pragma strict

var touchStart : Vector2;
var touchEnd : Vector2;
var flickTime = 5;
var flickLength = 0;
var ballVelocity : float;
var ballSpeed = 0;
var worldAngle : Vector3;
var ballPrefab : GameObject;
private var GetVelocity = false;
var whoosh : GameObject[];
var comfortZone : float;
var couldbeswipe : boolean;
var startCountdownLength : float = 0.0f;
var startTheTimer : boolean = false;
static var globalGameStart : boolean = false;
static var shootEnable : boolean = false;
private var startGameTimer : float = 0.0f;

function Start () {
	startTheTimer = true;
	Time.timeScale = 1;
		if ( Application.isEditor )
		Time.fixedDeltaTime = 0.01;
	}
		    	
function Update () {
	if (startTheTimer) {
		startGameTimer += Time.deltaTime;
	}
	if (startGameTimer > startCountdownLength){
		globalGameStart = true;
		shootEnable = true;
		startTheTimer = false;
		startGameTimer = 0;
	}	
	
	if (shootEnable) {
		Debug.Log ("enabled!");
		if (Input.touchCount > 0) {
			var touch = Input.touches[0];
       		switch (touch.phase) {
       			case TouchPhase.Began:
       				flickTime = 5;
       				timeIncrease();
       				couldbeswipe = true;
        			GetVelocity = true;
        			touchStart= touch.position;
        			break;
       			case TouchPhase.Moved:
        			if (Mathf.Abs(touch.position.y - touchStart.y) < comfortZone) {
        				couldbeswipe = false;
        			}
        			else {
        				couldbeswipe = true;
        			}
        		case TouchPhase.Stationary:
        			if (Mathf.Abs(touch.position.y - touchStart.y) < comfortZone) {
        				couldbeswipe = false;
        			}
        			break;
        		case TouchPhase.Ended:
        			var swipeDist = (touch.position - touchStart).magnitude;
        			if (couldbeswipe  swipeDist > comfortZone) {
						GetVelocity = false;
						touchEnd = touch.position;
						var ball = Instantiate(ballPrefab, Vector3(0,2.6,-11), Quaternion.identity) as GameObject;
						GetSpeed();
						GetAngle();
        				ball.rigidbody.AddForce(Vector3((worldAngle.x * ballSpeed), (worldAngle.y * ballSpeed), (worldAngle.z * ballSpeed)));
        				PlayWhoosh();
        				
        			}
        		}
        		if (GetVelocity) {
         			flickTime++;
        		}
		}
	}
	if (!shootEnable){
		Debug.Log("shot disabled!");
	}
}

function timeIncrease() {
	if (GetVelocity) {
		flickTime++;
	}
}

function GetSpeed() {
	flickLength = 90;
    if (flickTime > 0) {
        ballVelocity = flickLength / (flickLength - flickTime);
        }
    ballSpeed = ballVelocity * 30;
    ballSpeed = ballSpeed - (ballSpeed * 1.65);
    if (ballSpeed <= -33){
    	ballSpeed = -33;
    }
    Debug.Log("flick was" + flickTime);
    flickTime = 5;
}

function GetAngle () {
    worldAngle = camera.ScreenToWorldPoint(Vector3 (touchEnd.x, touchEnd.y + 800, ((camera.nearClipPlane - 100)*1.8)));
}

function PlayWhoosh(){
  var sound = Instantiate(whoosh[Random.Range(0,whoosh.length)],transform.position,transform.rotation) as GameObject;
  Debug.Log("Whoosh!");
}
1 Like

Thanks. I’ll try this out

Saving this, thanks :slight_smile:

Ya, I bookmarked it! Thanks Jeremy for the script!

This is amazing! Thanks very much. I’m already playing around with the code, chucking balls to a wall. I like to ask some questions about the script. How does it work in general? I know I have to attach it to a camera to set the direction of the ball. I can just edit the worldangle but I’m not sure what the values in there are for.

Also, what are these variables represent? Flick Time, Flick Length, Whoosh, Comfort Zone, Could be swipe, startcountdown length, and start the timer.

This is a new one to me, but I never knew I can actually use a gameobject to represent sound. I’ve always declare my audio clips in scripts. Is there a main difference between the two? and I also had to set it to play when awake to active the sounds.

Finally, I noticed that if I do circle gestures, the balls are thrown backwards? Try doing 2 circles and it might do the same. For the balls, it seems I can only click on the screen to throw but if I click at the top of the screen, the balls are still thrown at the middle. Is this where I have to adjust the comfort zone to change the position?

Thanks again for the code.

  • Hakimo

Hakimo - Thanks for checking it out. Let me start off by saying I am no code ninja, this should “kinda” work for anyone just by drag/dropping on a camera, but for accuracy and stuff you’ll definitely have to tweak some values. I’m a newbie so I kinda just made it work for our game.

The audio clip thing was an oops, because I was using a game object with sound attached to play our sounds. It ended up just being a gameobject with an audio clip attached, so you’re right it’s not necessary.

Ok, on to the nitty gritty. I meant to write a nice post about this and comment all the code but I forgot. I can do that now.

Flicktime represents the amount of time a finger has been touching the screen. I was originally using this measure speed of the throw. I ended up using it for the ballVelocity which then became the ballSpeed which then was multiplied by the world angle of the swipe to add the force to the ball. The longer your flicktime the longer the ball would go, because generally a longer touch meant a longer swipe. If the user ever just held their finger on the screen and only moved a bit, it would cancel out the touch as being a swipe.

I did a lot of tests with users using the game, and almost consistently I realized that a FAST long swipe and a slow short swipe generally are still separated enough using the flicktime counter. No matter how fast you flicked, if you flicked the entire screen the flicktime generally fell between a small range. Same with small fast / slow swipes. Am I making any sense?

whoosh was a random set of sounds I was playing for each time you flicked. in the inspector you can set a size for it, and attach however many game objects / audio clips you wanted and it would play a different random sound each time you swiped.

comfort zone was my way of canceling touches as being swipes. if it wasnt more than a certain pixel amount, it wasnt a swipe, so don’t throw a ball. everytime my players would hit the pause button it would launch balls. I was just too noob to find a better solution. It can still be useful though for other things, as with this script and without a comfort zone, a default touch can launch the ball a reasonable distance. I didn’t want my users just touching the screen to get cheap points. adding the default size minimum forced them to swipe a bit.

couldbeswipe was a way for me to tell while switching through the various touch phases if the touch was still a swipe. at certain times, (stationary, slow moves, etc) it might be set to false, canceling the swipe. in the end, if couldbeswipe was true, it would process the other routines to actually instantiate the ball and play the sounds, etc.

my game was a basketball type of shooter. you had ~30 seconds (or however long you wanted) to shoot as many balls into as many buckets as possible. when the game level launched though, i used a 4 second countdown (paired with on screen gfx) to show that the game was starting. similar to a race car game at the starting line.

basically it says, on awake start the timer and output that value to startGameTimer. then, once startGameTimer was greater than startCountdownLength, the game had started. startCountdownLength is a value you set to determine how long your “ready set go” time should be. you can set this in the inspector, and like I said I was using 4 seconds. once this happens, ive got a global variable set to true that activates other parameters in my score and game timer scripts. it also allows the shooting to even take place (shootEnable). in other scripts, and based on the gametimer, this is eventually set to false when time is up.

and you are right of course, this needs to be attached to a camera to work.

to adjust where it throws you need to fiddle with a few settings.
the GetSpeed and GetAngle functions will help you out here. I had to add certain numbers (+800 to Y, -100 / 1.8 to Z, etc) to the fields to make my game work. I’d suggest playing around with those numbers. Also look into the GetSpeed function because eventually ballspeed is multiplied by the worldAngle in GetAngle. These 2 things together determine the force added to the ball. I can imagine drawing circles might throw a ball backwards when you are moving your finger in the opposite direction of “screen up”? Again, comfortzone only determines the pixel amount necessary for something to register as a flick.

hope I answered a few of your questions. sorry for bad terminology, I’m not a coder by nature. id be happy to answer any more questions you have. :slight_smile:

jeremy

awesome! thanks for sharing!
i’ll give it a try when i got home =D

Managed to get it working, JeremyK this example is fab, thank you for sharing!

Always good to see someone sharing code, but it looks a bit complex (because it has all your game specific code in it).

EDIT: Plan to post a simple version here, but haven’t had time yet.

thanks for sharing .

Arrrrgh!! Cant get this to work on my iPad!! Can somebody post a working test scene so i could see how this should be setup?
As far as i understood this should be just attached to the camera and a ball connected to “ballprefab”??

Thanks for help!

what all components i need to add in order to see game running on my ipad/iphone…presently i am adding ball prefab and attaching above script to main camera…Is it right…? plz help

plz help unity3d forum

Can someone please explain me what are the values in the getAngle function
values “800”,“100”,“1.8”
What are they for. And why doesnt the object goes to the left side much even if I try too ??

Can you please tell me why those values are used in getangle function ??

Thanks for sharing this, I will try this tonight…I hope it will work.
Old thread but really been looking for a decent flick script for my game.

I have added this to my main camera then put a car as my gameobject but it does not work. Is there something else that I should be doing or does it only work for balls (cannot imagine it is)???

Hey JeremyK,
I was reading your post and thought I would add a C# version for those seeking it in that language also.
I hope you don`t mind, but as it is an implementation of your script but in another language i thought it would probably be okay.

Thanks again for the script, though this thread may benefit from a small usage tutorial - of which I haven`t currently got time to give and feel it would be better described by its original owner.

Thanks for the original script, I think its great.

As for optimizing the script a little, I have very quickly replaced your GameObject instantiation with a quick fire on an AudioClip that uses a compulsory AudioSource that will attach to the GameObject you apply this script to automatically.

Im sorry to all out there, I havent got time right now to flesh the comments out for readability.
hopefully, it will make sense as its not too far from your original one JeremyK.
Anyway, Thanks for reading .
C# converted with slightly different audio approach Script below:

using UnityEngine;
using System.Collections;
//demand an adio source be placed on this component for use with the audio clip
[RequireComponent(typeof(AudioSource))]
public class FlickThrowTouch : MonoBehaviour 
{

	public Vector2 touchStart;
	public Vector2 touchEnd;
	public int flickTime = 5;
	public int flickLength = 0;
	public float ballVelocity = 0.0f;
	public float ballSpeed = 0;
	public Vector3 worldAngle;
	public GameObject ballPrefab;

	private bool GetVelocity = false;
	//public GameObject[] woosh; //no
	public AudioClip ballAudio;  //yes
	public float comfortZone = 0.0f;
	public bool couldBeSwipe;
	public float startCountdownLength = 0.0f;
	public bool startTheTimer = false;
	static bool globalGameStart = false;
	static bool shootEnable = false;
	private float startGameTimer = 0.0f;

	private AudioSource asParamsControl;
	
	void  Start () 
	{
		asParamsControl = this.gameObject.GetComponentInChildren<AudioSource>();
		this.asParamsControl.playOnAwake = false;
		this.asParamsControl.loop = false;
		startTheTimer = true;
		Time.timeScale = 1;
		if ( Application.isEditor )
		{
			Time.fixedDeltaTime = 0.01f;
		}
	}
	
	void Update () 
	{
		if (startTheTimer) 
		{
			startGameTimer += Time.deltaTime;
		}
		if (startGameTimer > startCountdownLength)
		{
			globalGameStart = true;
			shootEnable = true;
			startTheTimer = false;
			startGameTimer = 0;
		}   
		
		if (shootEnable) 
		{
			Debug.Log ("enabled!");
			if (Input.touchCount > 0) 
			{
				var touch = Input.touches[0];
				switch (touch.phase) 
				{
				case TouchPhase.Began:
									flickTime = 5;
									timeIncrease();
									couldBeSwipe = true;
									GetVelocity = true;
									touchStart= touch.position;
									break;
				case TouchPhase.Moved:
									if (Mathf.Abs(touch.position.y - touchStart.y) < comfortZone) 
									{
										couldBeSwipe = false;
									}
									else 
									{
										couldBeSwipe = true;
									}
									break;
				case TouchPhase.Stationary:
									if (Mathf.Abs(touch.position.y - touchStart.y) < comfortZone) 
									{
										couldBeSwipe = false;
									}
									break;
				case TouchPhase.Ended:
									float swipeDist = (touch.position - touchStart).magnitude;
									if (couldBeSwipe  swipeDist > comfortZone) 
									{
										GetVelocity = false;
										touchEnd = touch.position;
										Rigidbody ball = Instantiate(ballPrefab, new Vector3(0.0f,2.6f,-11.0f), Quaternion.identity) as Rigidbody;
										GetSpeed();
										GetAngle();
										ball.rigidbody.AddForce(new Vector3((worldAngle.x * ballSpeed), (worldAngle.y * ballSpeed), (worldAngle.z * ballSpeed)));
										//PlayWhoosh();
										audio.PlayOneShot(ballAudio);	//yes play an audio clip once pre instantiation
										
									}
								break;
				default :
									break;
										break;

				}//end switch case
				if (GetVelocity) 
				{
					flickTime++;
				}
			}
		}
		if (!shootEnable)
		{
			Debug.Log("shot disabled!");
		}
	}
	
	void timeIncrease() 
	{
		if (GetVelocity) 
		{
			flickTime++;
		}
	}
	
	void GetSpeed() 
	{
		flickLength = 90;
		if (flickTime > 0) 
		{
			ballVelocity = flickLength / (flickLength - flickTime);
		}
		ballSpeed = ballVelocity * 30;
		ballSpeed = ballSpeed - (ballSpeed * 1.65f);
		if (ballSpeed <= -33)
		{
			ballSpeed = -33;
		}
		Debug.Log("flick was" + flickTime);
		flickTime = 5;
	}
	
	void GetAngle () 
	{
		worldAngle = camera.ScreenToWorldPoint(new Vector3 (touchEnd.x, touchEnd.y + 800.0f, ((camera.nearClipPlane - 100.0f)*1.8f)));
	}
	//no
//	void PlayWhoosh()
//	{
//		GameObject sound = Instantiate(whoosh[Random.Range(0,whoosh.length)],transform.position,transform.rotation) as GameObject;
//		Debug.Log("Whoosh!");
//	}
}


Take care all
Gruffy

hi i have looked through your code and was just wondering if there was anyway you help me narrow this down… i just want to have a simple swipe up function and make it do something AND a swipe down function and make that do something… any chance you could post it?

dude its not working i used this script