I have a working Unity script with multi-touch that will detect the touch, see if the touch has hit a collider (part of a gameObject), then checking to see if it is the right collider, and destroying it. Here is the working code:
using UnityEngine.UI;
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour
{
void Update () // Updates every frame
{
if (Input.touchCount != 0) // Triggered by a touch
{
foreach (Touch touch in Input.touches) // Triggered as many times as there are touches.
{
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(touch.position), Vector2.zero);
if (hit && touch.phase == TouchPhase.Began && hit.collider.gameObject.tag ==("Fish")) // Triggered if touch hits something, when touch just begins, and if the object hit was tagged as a "Fish"
{
hit.collider.gameObject.GetComponent<FishScript>().TappedOut(); // Fish script activated
}
}
}
}
}
This is the working code. Now I want to add a timer in there, and I want it to time the touch. I want to make it so if the player can tap, and move their finger over the fish and it will count, so long as the moved their finger over the fish in 1 second. Here is the script I wrote and need help with:
using UnityEngine.UI;
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour
{
void Update () // Updates every frame
{
if (Input.touchCount != 0) // Triggered by a touch
{
foreach (Touch touch in Input.touches) // Triggered as many times as there are touches.
{
if (touch.phase == TouchPhase.Began)
{
float touchTimer = Time.time;
int i = touch.fingerId;
}
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(touch.position), Vector2.zero);
if (hit && hit.collider.gameObject.tag ==("Fish")) // Triggered if touch hits something, when touch just begins, and if the object hit was tagged as a "Fish"
{
hit.collider.gameObject.GetComponent<FishScript>().TappedOut(); // Fish script activated
}
}
}
}
}
This is all I have so far. I tried using a return statement, and that did not work in the Void Update() function. I tried making another function with a while loop that would wait until each frame finished before going through another loop. That did not work. Will this idea work?
Here is a simple timer in Unity. Put CheckTimer() on your Update() method.
private double counter = 0.000d;
public double time = 2.000d; // How many seconds you want your timer to run
void CheckTimer()
{
counter+= Time.deltaTime;
if (counter >= time)
{
// Time has passed
counter = 0.000d;
// Do stuff here..
}
}
In your case, you may set a bool to true when your condition is met, then make it so that CheckTimer() only runs when the condition is true.
To fix this make the timer a global variable by instantiating it at the top and doing as follows
using UnityEngine.UI;
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour
{
public float timer;
public float duration;
void Start(){
duration = 1; //you can set this in code or in your hierarchy in Unity
}
void Update () // Updates every frame
{
if (Input.touchCount != 0) // Triggered by a touch
{
foreach (Touch touch in Input.touches) // Triggered as many times as there are touches.
{
if (touch.phase == TouchPhase.Began)
{
timer = Time.time + duration;
}
if(Time.time < timer){
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(touch.position), Vector2.zero);
if (hit && hit.collider.gameObject.tag ==("Fish")) // Triggered if touch hits something, when touch just begins, and if the object hit was tagged as a "Fish"
{
hit.collider.gameObject.GetComponent<FishScript>().TappedOut(); // Fish script activated
}
}
}
}
}
}
I don't have the rest of your code to ensure this works but the logic behind it does. You want to set your timer to the current time plus a given duration. If the current time is less than the timer you have set when the player does something, then you know they did their action before the time reached the timer. Does that make sense?
I know I'm not suppose to take up comment spaces by saying thanks, but thanks Jonnus. The only thing that baffles me now is how I didn't think of this sooner. Anyways, here is the take away code for anybody who needs help with a multi-touch script.
using UnityEngine.UI;
using UnityEngine;
using System.Collections;
Private float timer; // Control variable
Public float duration; // How long you want your touch to be valid
Void Start ()
{
duration = 1; // Also adjustable in the interface
}
Void Update ()
{
if (Input.touchCount != 0) // Triggered by a touch
{
Foreach (Touch touch in Input.touches) // Triggered as many times as there are touches
{
if (touch.phase == TouchPhase.Began)
{
timer = Time.time + duration;
}
RaycastHit2D objectPlayerTouched = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(touch.position), Vector2.zero);
// var objectPlayerTouched is the 2DCollider for whatever the player tapped on.
if (objectPlayerTouched != null && Time.time < timer) // Triggered if player taps an object and time is still valid
{
objectPlayerTouched(destroy); // This means whatever object the player tapped is now destroyed
}
}
}
}
Once again, a big thanks to Jonnus. I thought this would be a lot more complicated. Also, don't do what I did and forget to set the duration in the interface and end up spending 10 minutes looking for a problem... Trust me it's annoying.
Related
I started to work on my first mobile game in Unity. For now, the game I am working is Pong.
The problem that I am having is that I want to create the pause and resume buttons but whenever I create and tap on them, nothing happens.
This is the created button:
This is my canvas:
This is the event system:
This is the code I use for my button. It works fine when I clic it but not when I tap it:
private void pause() {
Time.timeScale = 0f;
}
private void resume()
{
Time.timeScale = 1f;
}
public void pauseResume() {
if (!paused)
{
pause();
paused = true;
}
else {
if (paused)
{
resume();
paused = false;
}
}
}
I tried using Event triggers but it does not work so my theory is that the problem is not in the way I create the buttons or the way the resume pause method is coded. Something is blocking me of using the button in my device (Iphone 8).
OK. Looked through the possible answers, but I don't see my issue here.
I have a fairly bog-standard GCD repeating timer:
class RepeatingGCDTimer {
/// This holds our current run state.
private var state: _State = ._suspended
/// This is the time between fires, in seconds.
let timeInterval: TimeInterval
/// This is the callback event handler we registered.
var eventHandler: (() -> Void)?
/* ############################################################## */
/**
This calculated property will create a new timer that repeats.
It uses the current queue.
*/
private lazy var timer: DispatchSourceTimer = {
let t = DispatchSource.makeTimerSource() // We make a generic, default timer source. No frou-frou.
t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval) // We tell it to repeat at our interval.
t.setEventHandler(handler: { [unowned self] in // This is the callback.
self.eventHandler?() // This just calls the event handler we registered.
})
return t
}()
/// This is used to hold state flags for internal use.
private enum _State {
/// The timer is currently paused.
case _suspended
/// The timer has been resumed, and is firing.
case _resumed
}
/* ############################################################## */
/**
Default constructor
- parameter timeInterval: The time (in seconds) between fires.
*/
init(timeInterval inTimeInterval: TimeInterval) {
self.timeInterval = inTimeInterval
}
/* ############################################################## */
/**
If the timer is not currently running, we resume. If running, nothing happens.
*/
func resume() {
if self.state == ._resumed {
return
}
self.state = ._resumed
self.timer.resume()
}
/* ############################################################## */
/**
If the timer is currently running, we suspend. If not running, nothing happens.
*/
func suspend() {
if self.state == ._suspended {
return
}
self.state = ._suspended
self.timer.suspend()
}
/* ############################################################## */
/**
We have to carefully dismantle this, as we can end up with crashes if we don't clean up properly.
*/
deinit {
self.timer.setEventHandler {}
self.timer.cancel()
self.resume() // You need to call resume after canceling. I guess it lets the queue clean up.
self.eventHandler = nil
}
}
Works great!
...except when it doesn't.
That would be when I put the device into Airplane Mode.
At that point, the timer stops firing.
Even when I come out of Airplane Mode, the timer doesn't restart.
The app uses UIApplication.shared.isIdleTimerDisabled = true/false to keep the app awake, but that doesn't seem to keep the events coming.
Can anyone clue me into what's happening here, and how I might be able to work around it?
This app needs to work in Airplane Mode. In fact, it is most likely to be used in Airplane Mode.
OK. I think I solved this. As is often the case with these things, it's PEBCAK.
I have a routine that stops the timer when the app is backgrounded, and failed to put in a corresponding restart for when it is foregrounded.
When I slide up the Control Center, it backgrounds the app.
My bad.
Yeah, it's embarrassing, but I want to leave this question here as a warning to others.
I'm trying to create a swipe function to work in iOS devices. I've calculated the start touch position and the end touch position. Now, I want to calculate the start time and end time to get a time difference between the two touch events.
I've tried Time.time and Time.deltatime but as You already know It didn't work. So Please Help. Or if have any better suggestions for swipe, then please suggest me.
Thank You.
I believe what brandon is implying is you need to record Time.time in a variable such as startTime where you detect start touch then where you are recording the end touch do something like:
diffTime = Time.time - startTime
EDIT: Based on your comment I'm assuming it didn't work. Without your code its hard to say where you're getting lost but I'll try a psuedo code example in C# (don't know whether you're using Boo, C#, or Javascript):
public class Example : MonoBehaviour {
public float startTime;
public float diffTime;
void Update() {
if (/* DETECT TOUCH START */ ) {
startTime = Time.time; // Record startTime in secs since game start.
}
if (/* DETECT TOUCH END */ ) {
diffTime = Time.time - startTime; // Record diffTime in secs since startTime.
Debug.Log("startTime / diffTime = " + startTime + " / " + diffTime);
startTime = 0; // Reset startTime.
}
}
}
I'm working on a basic flash fight game. I'm not a developer so i'm new to actionscript except that i have some background knowledge about coding from my course.
The problem is-
There are about 6 fighting moves and i want to disable all 6 key_down events till the animation completes. And all 6 animations have different time frames. Can some1 just help me out with this?
stage.addEventListener(KeyboardEvent.KEY_DOWN, enterKeyHandler);
function enterKeyHandler(event:KeyboardEvent):void {
if (event.keyCode == Keyboard.B) {
gotoAndPlay(252);}
if (event.keyCode == Keyboard.V) {
gotoAndPlay(259);}
I have put down only 2 of them but there 6 in total.
I would use a public static flag , ex:
keyboardDisabled:Boolean = false;
While you play your animations you could set this to true, and inside the function which listens to keyboard events just check if keyboard is disabled and return right away.
Some code would look like
public static var keyboardDisabled:Boolean = false;
stage.addEventListener(KeyboardEvent.KEY_DOWN, enterKeyHandler);
function enterKeyHandler(event:KeyboardEvent):void {
if ( keyboardDisabled )
return;
if (event.keyCode == Keyboard.B) {
gotoAndPlay(252);}
if (event.keyCode == Keyboard.V) {
gotoAndPlay(259);
}
Then in the frame where your animation begin
keyboardDisabled = true;
And on animation end
keyboardDisabled = false;
I'm creating a game in XNA that runs on a PC.
On the splash screen, the user has three options. If they press "Enter" the game will begin, if they press "M" they'll go to the Help menu and if they press "W" I want that to take them to my website.
I'm using Process.Start to open the browser to my website.
The problem is that when I press "W", sometimes it will open 1 browser with the website. However, most of the time it will open anywhere from 3 - 7 browsers simultaneously.
Why is it opening multiple browsers simultaneously?
How do I make it open only 1 browser when "W" is pressed?
Here is my code. I haven't built my website yet, so I'm using yahoo.com as the destination:
private void UpdateSplashScreen()
{
KeyboardState keyState = Keyboard.GetState();
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W))
{
Process.Start("IExplore.exe", "www.yahoo.com");
}
}
Thanks,
Mike
A common way to handle this is to always track the keyboard state from the previous frame. If a key wasn't down on the previous frame, but is down this frame then you know it was just pressed. If the key was down on the previous frame then you know it's being held down.
// somewhere in your initialization code
KeyboardState keyState = Keyboard.GetState();
KeyboardState previousKeyState = keyState;
...
private void UpdateSplashScreen()
{
previousKeyState = keyState; // remember the state from the previous frame
keyState = Keyboard.GetState(); // get the current state
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter) && !previousKeyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M) && !previousKeyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W) && !previousKeyState.IsKeyDown(Keys.W))
{
Process.Start("IExplore.exe", "www.yahoo.com");
}
}
I usually create a KeyPressed function which cleans things up a bit.
bool KeyPressed(Keys key)
{
return keyState.IsKeyDown(key) && !previousKeyState.IsKeyDown(key);
}
The code you are using runs about 60 times a second; you may only press your key down for 100ms or so but in that time it checks to see if W is pressed down about 7 times. As such, it opens a large number of browser windows.
Try recording a timestamp (using DateTime.Now) of when you open the browser and then check that a certain time has elapsed (~2 secs?) before allowing another window to be opened. Or, create a boolean flag that is set false by opening the browser, so the browser can be opened only once.
Thanks guys, that's what the problem was.
Callum Rogers solution was the easiest:
I declared a boolean:
bool launchFlag = false;
Then checked it and set it to true after the website launched.
private void UpdateSplashScreen()
{
KeyboardState keyState = Keyboard.GetState();
if (gameState == GameState.StartScreen)
{
if (keyState.IsKeyDown(Keys.Enter))
{
gameState = GameState.Level1;
explosionTime = 0.0f;
}
if (keyState.IsKeyDown(Keys.M))
{
gameState = GameState.HelpScreen;
}
if (keyState.IsKeyDown(Keys.W))
{
if (launchFlag == false)
{
Process.Start("IExplore.exe", "www.yahoo.com");
launchFlag = true;
}
}
}
I held the W key down for 30 seconds and it launched just 1 browser!
Thanks,
Mike