I'm developing a game on Unity for iOS devices. I've implemented the following code for touch:
void Update () {
ApplyForce ();
}
void ApplyForce() {
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began) {
Debug.Log("Touch Occured");
}
}
I've dragged this script onto my game object which is a sphere. But the log message appears no matter where I touch. I want to detect touch only when the users touches the object.
stick the code bellow in your camera, or something else that will persist in the level you're designing. It can be anything, even an empty gameobject. The only important thing is that it only exists ONCE in your level, otherwise you'll have multiple touch checks running at the same time, which will cause some seriously heavy load on the system.
void Update () {
if (Input.touchCount > 0 && Input.GetTouch (0).phase == TouchPhase.Began)
{
Ray ray = Camera.main.ScreenPointToRay( Input.GetTouch(0).position );
RaycastHit hit;
if ( Physics.Raycast(ray, out hit) && hit.transform.gameObject.Name == "myGameObjectName")
{
hit.GetComponent<TouchObjectScript>().ApplyForce();
}
}
}
In the above script, hit.transform.gameObject.Name == "myGameObjectName" can be replaced by any other method you want to check that the object hit by the raycast is the object you want to apply a force to. One easy method is by Tags for example.
Another thing to keep in mind is that raycast will originate at your camera (at the position relative to your finger's touch) and hit the first object with a collider. So if your object is hidden behind something else, you won't be able to touch it.
Also put the script bellow in your touch object (in this example, called TouchObjectScript.cs)
void Update () {
}
public void ApplyForce() {
Debug.Log("Touch Occured");
}
If something wasn't clear, or you need any further help, leave a comment and I'll get back to you.
The best way to do this, is just adding:
void OnMouseDown () {}
to the gameobject you want to be clicked.
The problem with this is that you cannot press 2 things at one time.
Related
I'm using a simple turn based loop, switching between player turn and enemy turn. In the player turn the loop should "pause" until framework function touchDown has been activated, then pass the turn to the enemy. I'm not sure how to implement this as touchDown triggers automatically whenever you press the screen.
Am I to use an observer of some sort or a #selector? Tried both unsuccessfully, but that may just be for my lack of experience. If I use the selector the loop doesn't pause to wait for the screen to be clicked. As for the observer, I'm just not sure how its implemented. I also tried just adding the whole touchDown method into the player turn but that obviously didn't work.
The loop simply looks like this:
var state = "PLAYER"
func runGame() {
while(gameIsProgressing) {
if(state == "PLAYER") {
print("Player is doing stuff")
//THE touchDown METHOD SOULD BE TRIGGERD HERE
state = "ENEMY"
}else if(state == "ENEMY") {
print("\n🙈ENEMY IS DOING STUFF🙈")
state = "PLAYER"
}
}
}
Also, it is important that touchDown is triggerd only in the player turn, and not whenever the screen is pressed.
Any ideas?
Since you are programming something with a GUI here, you should not think in this imperative way. There is no (good) way to "pause" a while loop "until" something happens. You should think in a more event-driven way.
Instead of thinking about the while loop, think in terms of the events that happen. In this case, the event is touchDown. Note that I don't know of a library function called touchedDown (did you mean touchesBegan or is this an action for a UIControl?), but I'll assume it exists. When the player touches the screen, what do you want to happen? Well, probably something like this:
guard gameIsProgressing else { return }
if(state == "PLAYER") {
someFunctionThatShowsWhatThePlayerDid()
state = "ENEMY"
print("\n🙈ENEMY IS DOING STUFF🙈")
someFunctionThatMakesTheEnemyDoStuff(completion: {
self.state = "PLAYER"
})
}
I couldn't find an implementation of a double tap for Appium that was straightforward and allowed you to pass in the element locator strategy, so here goes:
public static void doubleTapElementBy(By by) {
WebElement el = getDriver().findElement(by);
MultiTouchAction multiTouch = new MultiTouchAction(getDriver());
TouchAction action0 = new TouchAction(getDriver()).tap(el).waitAction(50).tap(el);
try {
multiTouch.add(action0).perform();
} catch (WebDriverException e) {
logger.info("Unable to do second tap on element, probably because element requieres single tap on this Android version");
}
}
You can also try below approach using tap method in TouchAction class.
TouchAction taction = new TouchAction(driver);
taction.tap(tapOptions().withElement(ElementOption.element(YOUR_WebElement))
.withTapsCount(2)).perform();
You will need to add below static import as well:
import static io.appium.java_client.touch.TapOptions.tapOptions;
This is a workaround in pseudocode and possibly there's a more "official" way to do it, but it should do the work if no other solution is available:
Interpretmessages(){
switch(msg)
{
OnClick:
{ if (lastClicked - thisTime() < 0.2) //if it was clicked very recently
{doubleTapped()} //handle it as a double tap
else{lastClicked = thisTime()} //otherwise keep the time of the tap
} //end of OnClick
} //End of Message Handler
}//End of switch
}//End of messageHandler
If you have access to ready timer functions, you can set a function to be executed 0.2s after the click has gone off:
OnClick: if (!functionWaiting) // has the timer not been set?
{
enableTimer(); // set a function to go off in x time
clicks = 0; //we'll tell it that there's been one click in a couple of lines
} //set it for the first click
clicks++; //if it's already clicked, it'll become 2 (double tap) otherwise it's just one
So, the idea is that when you get a tap, you check if there's been another one recently (a. by checking the relative times, b. by checking if the function is still pending) and you handle it dependingly, only note that you will have to implement a timer so your function fires a bit later so you have time to get a second tap
The style draws upon the Win32's message handling, I'm pretty sure it works there, it should work for you too.
Double tap and hold -- Use below code:
new TouchAction(driver).press(112,567).release().perform().press(112,567).perform();
Double tap -- Use below code:
new TouchAction(driver).press(112,567).release().perform().press(112,567).release().perform();
I'm working with the Blackberry Touch Event and I need to process the TouchEvent.MOVE, TouchEvent.UP and TouchEvent.DOWN to move a carousel of images and the TouchEvent.CLICK to generate some specific action.
My problem is that the touchEvent() method is being called several times. How can I prevent this? Because the behaviour is getting all messed up.
For example: when I just want to capture the TouchEvent.CLICK event, the UP-DOWN-MOVE-CLICK are being triggered one after the next.
My code does the following:
protected boolean touchEvent(TouchEvent message) {
if (message.getEvent() == TouchEvent.CLICK) {
//CHANGE THE CONTENT OF A FIELD
return true;
} else if ((message.getEvent() == TouchEvent.MOVE)
|| (message.getEvent() == TouchEvent.UP)
|| (message.getEvent() == TouchEvent.DOWN)) {
//DELETE THE FIELD
//MOVE A CAROUSEL OF IMAGES
} else {
return false;
}
}
public void moverTouch(int dx) {
//ADD THE FIELD PREVIOUSLY DELETED
}
As you can see, when the CLICK event is captured, I need to change the content of a field, but when the MOVE or UP or DOWN event is captured, I need to delete that Field from his manager, do some working with a carousel of images, and then re-add the field previously deleted.
The carousel moving part works fine, but when I try to capture JUST the CLICK event, and the others are being triggered as well, but the moverTouch() function doesn't get triggered because there is no actual movement on the carousel of images, I end up with a deleted field that I need to update its content.
At a glance:
I think you need to differentiate touch gestures (I believe you are "listening" for something like a swipe gesture to scroll the gallery image) from "normal" touch events (specifically you need to process TouchEvent.CLICK to apply "some specific action" to the clicked image).
To decide whether the TouchEvent represents a gesture there is a TouchEvent.GESTURE constant. Then it is possible to know what exactly TouchGesture it represents by calling TouchEvent.getGesture().
Some sample code for touch gestures can be viewed here.
I have some problem with my back button. In my game I have two screens. One with title (menu) and second with the game. When I use once back button i pause game and back to title screen. When i use it again I need to kill the app process. How can i do that? Down below i show u how I use back button. I try use 2 gestures, but when I declared them nothing go right.
That's how i declared gesture in Initialize():
TouchPanel.EnabledGestures = GestureType.FreeDrag
First I declared
bool IsPlayingGame = true;
int endGame= 0;
Then in function Update:
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
if (!IsPlayingGame) this.Exit();
}
if (isTitleScreenShown)
{
UpdateGameScreen();
}
else if (isGameSceenShown)
{
UpdateTitleScreen();
// TODO: Add your update logic here
/* MY FUNCTIONS */
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
{
if (endGame == 5) base.Exit();
}
}
base.Update(gameTime);
In Visual studio, it works. I thought great, but on Nokia phone it doesn't. Why? Any help?
In the GamePage.xaml.cs there should be a method protected override void OnNavigatedFrom(NavigationEventArgs e)
This method is triggered when you touch the 'back' button on your device.
I'm just going to explain the context so it is clearer.
I made this menu : my menu
I am looking to make an improved and more advanced version of the same menu.
I made an animation of waves on the cofee's surface and am looking to make it loop when the mouse is moving and to stop looping when it's not.
Sorry for the lack of specifications as I am quite new to actionscript, but I hope somebody will be able to help me. :)
Thanks,
Mathieu
Well, you said it - leverage MouseEvent.MOUSE_MOVE to set a conditional in your looping routine.
private var _isMoving:Boolean = false;
stage.addEventListener(MouseEvent.MOUSE_MOVE, checkMouse);
this.addEventListener(Event.ENTER_FRAME, doLoop);
private function checkMouse(e:MouseEvent):void
{
_isMoving = true;
}
private function doLoop(e:Event):void
{
trace("moving =" + _isMoving);
if(_isMoving)
{
// loop animation
}
_isMoving = false;
}
depending on how you want it to work I would do this as follows:
create an animation of wavy coffee
ensure the animation loops
note that clips loop by default, so all you have to do is match the first and last frames!
place the clip at the edge of your current coffee graphic
double click on the graphic to edit it
drag an instance of the looping animation from the library onto the "edge" of the graphic
OR just replace your entire light brown graphic with an animated one that loops
when the mouse is moving, call play on the animated loop clip
when the mouse stops, call stop on the animated loop clip
Some example code would be along the lines of:
public function init():void {
menuClip.addEventListener(MouseEvent.MOUSE_OVER, onMenuRollOver);
menuClip.addEventListener(MouseEvent.MOUSE_OUT, onMenuRollOut);
}
public function onMenuRollOver(event:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
/* do the stuff you're currently doing to animate the clip here.
something like: coffee graphic height = ease to mouseHeight */
}
public function onMenuRollOut(event:MouseEvent):void {
/* do the stuff you're currently doing to stop the clip here. */
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
coffeeClip.stop();
}
public function onMove(event:MouseEvent):void {
resetTimer();
coffeeClip.play(); //note: play has no effect when movie is playing (that's ideal in this case)
}
public function resetTimer():void {
if(mouseMovementTimer == null) createTimer();
mouseMovementTimer.reset();
mouseMovementTimer.start();
}
public function createTimer():Timer {
mouseMovementTimer = new Timer(DELAY, 1); //fiddle with the delay variable. Try 500, at first
mouseMovementTimer.addEventListener(TimerEvent.TIMER, stopAnimationLoop);
}
public function stopAnimationLoop(event:TimerEvent):void {
mouseMovementTimer.removeEventListener(TimerEvent.TIMER, stopAnimationLoop); //optional but recommended
mouseMovementTimer = null;
coffeClip.stop();
}
Of course, you would need to do things like call init() and import flash.utils.Timer and initialize variables like mouseMovementTimer, menuClip, coffeeClip and DELAY.
Warning: This code is off the top of my head and untested. So there's likely to be small bugs in it but you should get the general idea:
add a mouse listener when the user mouses over the menu
remove that listener if the user mouses out of the menu
have that listener play the looping movie clip
trigger an event that will stop the looping clip if movement hasn't been detected in a while
once the trigger goes of, stop the clip
The key is in detecting when the mouse stops moving. Flash detects interaction well but does not detect NON-INTERACTION for obvious reasons. One easy way to solve that is to trigger a timer that will go off once too much time has elapsed since the last activity. Then, when the timer triggers, you know action has stopped!
I think that's the key piece to solving your problem. I hope that helps someone in some way.
~gmale