I've been exploring into Nim Game on Android. The players are going to take objects from heaps. I use openGLES to draw the objects and heaps. Where I'm stuck is how to "take".
As the samples shown on official dev guide
, I can override onTouchEvent method in the class that extends GLSurfaceView for the interaction. However, how I could tell where the objects have been drawn? Or are there any objects at the coordinates where I touch?
Any ideas?
Thx in advance!
If I'm understanding your question correctly, it sounds like you want to do some simple collision detection to see if your touch point is inside one of the objects on the heap. You can do this with some basic math between the coordinates of the touch point and the center coordinates which you used to draw the object.
For instance, assuming your objects are rectangles, this would be the general idea:
boolean detectCollision(Object object, TouchPoint touch) {
return object.x - object.width/2 <= touch.x &&
object.x + object.width/2 >= touch.x &&
object.y - object.height/2 <= touch.y &&
object.y + object.height/2 >= touch y;
}
You could then iterate through all of the objects in your heaps and if this returns true for any of them, then you know your touchpoint is inside of that object and can proceed to call whatever you need to call on it.
Keep in mind that the touch coordinates the system gives you will be screen coordinates, so you have to take into account any discrepancies between the screen coordinate system and the coordinate system you defined with your view frustum.
public class Main extends Activity implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
synchronized (this) {
if (!_isPaused) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
_touchedX = event.getX();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
float touchedX = event.getX();
float dx = Math.abs(_touchedX - touchedX);
_dxLowPassed = lowPass(dx, _dxLowPassed);
switch (_screenUsage) {
case HALF_SCREEN:
if (touchedX < _width / 2) {
if(touchedX < _touchedX) {
_zAngle = (2 * _dxLowPassed / _width) * TOUCH_SENSITIVITY * ANGLE_SPAN;
_zAngleLowPassed = lowPass(_zAngle, _zAngleLowPassed);
GLES20Renderer._zAngle = GLES20Renderer._zAngle + _zAngleLowPassed;
}
} else {
if( touchedX > _touchedX ) {
_zAngle = (2 * _dxLowPassed / _width) * TOUCH_SENSITIVITY * ANGLE_SPAN;
_zAngleLowPassed = lowPass(_zAngle, _zAngleLowPassed);
GLES20Renderer._zAngle = GLES20Renderer._zAngle - _zAngleLowPassed;
}
}
Log.d("TOUCH", new Float(_zAngleLowPassed).toString());
break;
Related
There's someone here can help me. Im new to unity. I was working on making objects moves only on x-axis / y-axis when the object touch and drag to the left the object moves to left and stops when the touch or removes on the object.
using UnityEngine;
using System.Collections;
public class MoveScriptVertical : MonoBehaviour
{
//touch offset allows ball not to shake when it start moving
float deltax,deltay;
Rigidbody2D rb;
bool moveAllowed = false;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
}
void Update ()
{
//touch event
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch (0);
//knowing the touch position
Vector2 touchPos = Camera.main.ScreenToWorldPoint(touch.position);
//touch phase
switch (touch.phase)
{
case TouchPhase.Began:
//if you touch the car
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos))
{
//get the offset between position you touch
deltax = touchPos.x - transform.position.x;
deltay = touchPos.y - transform.position.y;
//if touch began within the car collider
//then it is allowed to move
moveAllowed = true;
//restriction some rigidbody properties
rb.freezeRotation = true;
rb.velocity = new Vector2 (0, 0);
GetComponent<BoxCollider2D> ().sharedMaterial = null;
}
break;
//you move your finger
case TouchPhase.Moved:
//if you touches the car and move is allowed
if (GetComponent<Collider2D> () == Physics2D.OverlapPoint (touchPos) && moveAllowed)
rb.MovePosition (new Vector2 (0, touchPos.y - deltay));
break;
//you released your finger
case TouchPhase.Ended:
//restore intial parameters
//when touch is ended
moveAllowed = false;
rb.freezeRotation = true;
rb.gravityScale = 0;
PhysicsMaterial2D mat = new PhysicsMaterial2D ();
GetComponent<BoxCollider2D> ().sharedMaterial = mat;
break;
}
}
}
}
There are some libraries that allow you to have complex finger events, for instance Touchscript. If you choose this solution you will find tutorials online.
The most basic way is to use unity's Input class with a nice doc here: https://docs.unity3d.com/ScriptReference/Input.html
This allow you to know when a finger touched the screen or moved on the screen. It will give you x an y coordinate so if you just want to move along x you would use only the x value, and, for example translate your object with it using
transform.Translate(new Vector3(x, 0f, 0f));
Edit
Since you use touchscript, have a look at TransformGesture, it should allow you to handle many transform events. If you want to have more control, you can use PressGesture, PanGesture and ReleaseGesture instead
How to show a response while gesture input is in progress?
I'm using swipe down gesture to reload my WebView.
Using this for gesture detection:How to detect swipe direction between left/right and up/down
Problem
SimpleGestureListener captures the result of user input only. It cannot be used to show a response, e.g. animation, while the user is performing a gesture.
Imperfect inelegant solution:
flaw: Shows animation independent of SimpleOnGestureListener; a response to user gesture input is displayed and gesture input may still fail or vice versa.
private volatile float userTouchY = -1;
// translating WebView every onTouch event call is inefficient.
// only translate when translateYBy value is greater than a threshold.
private float translateYBy = 0;
/**
* Shift webView without animation.
* #param num
*/
private void __shiftWebViewDownBy(int num){
float current = webView.getY();
if(current >= webviewTranslationLimit) return;
current += num;
if(current> webviewTranslationLimit) current = webviewTranslationLimit;
webView.setY(current);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
//Skip everything unless at the content top.
if(webView.getScrollY() > 0) return false;
int action = event.getAction();
if(action == MotionEvent.ACTION_MOVE){
float y = event.getY();
float dif = y - userTouchY;
if(dif < webviewTranslationLimit/10){
//dif less than screenHeight*1/40, ignore input
}else if(dif < -(webviewTranslationLimit/5)){
//swipe up
userTouchY = -1;//not swipe down cancelling
__shiftWebViewTopTo(0);
}else if(userTouchY < y) {
//swipe down
translateYBy += dif;
if(userTouchY == -1){
//userTouchY at the initial value, ignore for this once.
userTouchY = y;
}else{
userTouchY = y;
if(translateYBy > 5 ){
__shiftWebViewDownBy((int)translateYBy);
translateYBy = 0;
}
}
}
}else{
Log.d(TAG,"action not move:" +MotionEvent.actionToString(action));
// Webview shift down should only occur while moving
// Once finger is off,cancel
__shiftWebViewTopTo(0);
userTouchY = -1;
}
boolean result = gestureDetector.onTouchEvent(event);
return result;
}
Writing a GestureListener of a sort from scratch obviously solves the problem and I can simply use Toast to message the user for a failed gesture input, but there ought to be an easier, more readable solution.
I am using MPAndroidChart for displaying a line chart for my application. What I already have is a double tap listener for the whole chart.
Is there any way to obtain double tap listener control for an individual point in line chart? My best guess is to directly obtain the View object of the point and implement a gesture listener for the same. If that is plausible, how can I achieve that?
If this is not possible using MPAndroidChart, is there any other library that might help me with this?
Your solution would involve creating a class that implements OnChartGestureListener
You can immediately see there is a method:
void onChartDoubleTapped(android.view.MotionEvent me);
You would have to implement this method with the functionality you want. It would probably involve getting the raw pixel touch points from the MotionEvent and converting them to x and y values on the chart. There is instructions on how to do that in this answer and in the code below:
#Override
public void onChartDoubleTapped(MotionEvent me) {
float tappedX = me.getX();
float tappedY = me.getY();
MPPointD point = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(tappedX, tappedY);
Log.d(TAG, "tapped at: " + point.x + "," + point.y);
}
Further examples of a custom OnChartGestureListener are inside the example project here
David Rawson's answer is just perfect and answers the question leaving no stones unturned. However, for my situation, I found the answer by firegloves more useful. I actually get the Entry object using firegloves' solution to obtain values from. Following is the code for what solved my problem but please do note that this solution is hacky and is not advised until absolutely necessary. If you want a clean solution, David Rawson's answer already covers that.
mChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
#Override
public void onValueSelected(Entry e, Highlight h) {
final long selectionTime = System.currentTimeMillis();
final Highlight copyHighlight = h;
final Entry copyEntry = e;
final boolean[] isTapped = {false};
mChart.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (!isTapped[0]) {
final long doubleTapTime = System.currentTimeMillis();
if (doubleTapTime - selectionTime < 300) {
if ((motionEvent.getX() <= copyHighlight.getDrawX() + 50
&& motionEvent.getX() >= copyHighlight.getDrawX() - 50)
&& (motionEvent.getY() <= copyHighlight.getDrawY() + 50
&& motionEvent.getY() >= motionEvent.getY() - 50)) {
if (copyEntry.getX() == 1.0f && copyEntry.getY() == 1.0f) {
Toast.makeText(MainActivity.this, "1,1", Toast.LENGTH_SHORT).show();
} else if (copyEntry.getX() == 2.0f && copyEntry.getY() == 2.0f) {
Toast.makeText(MainActivity.this, "2,2", Toast.LENGTH_SHORT).show();
} else if (copyEntry.getX() == 3.0f && copyEntry.getY() == 3.0f) {
Toast.makeText(MainActivity.this, "3,3", Toast.LENGTH_SHORT).show();
}
}
}
}
isTapped[0] = true;
return false;
}
});
}
#Override
public void onNothingSelected() {
}
});
The code snippet above let me work with the Entry object which was associated with the point that was double tapped. Also, I have set a range of 100 pixels while the point was double tapped for the convenience of fat-finger tapping.
I'd love to know if there is a clean solution for double tapping a value while obtaining its Entry data.
Deeping into LineChartRenderer I think it's not possible because graph rendering is reached drawing directly on Canvas like follows:
canvas.drawPath(mCirclePathBuffer, mRenderPaint);
You could try to print all your graph children to check if I'm wrong, something like:
int size =lineChart.getChildCount();
for (int i=0; i<size; i++) {
View v = lineChart.getChildAt(i);
Log.i("### LINE", "### INSTANCE OF " + v.getClass());
}
If there are children views they must be accessible it this manner.
Otherwise you could try to intercept double tap on your entire LineGraph, then you could try to check if tap position correspond to a mapped position but I think you must do the entire work on your own.
Another options could be to implement OnChartValueSelectedListener and emulate double tap with a timer mechanism.
I don't think there is a library that supports this functionality.
Let me know if you find a solution please
After getting the calculator application to work I decided to try to create pong. There is a box in the center and two paddles on both ends. The phone is horizontal. I have the box bouncing off the walls and the paddle moves with me moving my finger down. My problem is i want to make it two player and i want to have multiple finger input for the game. I want one finger to move paddle 1 and the other to move paddle 2. So far this is my input code
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
float p1y = ev.getY();
if(ev.getX()<300)
{
player1y = p1y;
}
if(ev.getX()>300)
{
player2y = p1y;
}
//player1y = p1y;
invalidate();
break;
}
}
return true;
}
it resides in my surfaceview class. How can i modify the input method or completely get rid of it and change it to accomplish my goal? Also sorry about my variables. Eclipse crashes a lot on me and my laptops touch panel tends to move my cursor so shorter variables seemed viable. p1y is the y of the touch. and player1y and player2y is the y positions of the player1 and player2 paddle.
A MotionEvent can hold multiple pointers. Use getPointerCount() to see how many pointers are touching the screen in the current event. There are alternate versions of getX and getY that take an index from 0-getPointerCount() - 1.
In a more complex app you would want to track fingers by pointer ID, but for something this simple where you are using a cutoff point on the screen you could do something like this in your ACTION_MOVE case:
int pointerCount = ev.getPointerCount();
for (int i = 0; i < pointerCount; i++) {
float x = ev.getX(i);
float y = ev.getY(i);
if (x < 300) {
player1y = y;
} else if (x > 300) {
player2y = y;
}
}
This post from the Android Developers Blog might help if you'd like more information: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html
Can anyone help please.
Im writing a small Android game where the player is able to select a "barrier" and drag it accross the screen with their finger. I have the barriers drawn on the screen and I am able to drag it accross the screen.
My problem however is when I add more than 1 barrier, eg 3 barriers, and drag a barrier accross the screen, they all drag and they all drag to the same position. That is to say they all lie on top of each other, making it look as though there is only 1 barrier.
Here is my code, can anyone please tell me where I am going wrong/explain where I am going wrong.
public class MainGamePanel extends SurfaceView implements SurfaceHolder.Callback, SensorEventListener {
// Initialising the Barrier
private Barrier barrier[] = new Barrier[3];
// The Main Game Panel
public MainGamePanel(Context context) {
super(context);
// Adding a call-back (this) to the surfaceHolder to intercept events
getHolder().addCallback(this);
// Creating the Game items
// The starting coordinates of the Barrier
int x = 30;
int y = 270;
barrier[0] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.blue_barrier), x, y);
barrier[1] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.green_barrier), x + 15, y);
barrier[2] = new Barrier(BitmapFactory.decodeResource(getResources(), R.drawable.pink_barrier), x + 30, y);
// Create the Game Loop Thread
thread = new MainThread(getHolder(), this);
// Make the GamePanel focusable so it can handle events
setFocusable(true);
}
// Handles the touch events
public boolean onTouchEvent(MotionEvent event)
{
int eventAction = event.getAction();
int x = (int)event.getX();
int y = (int)event.getY();
switch (eventAction)
{
// Touch down so check if finger is on Barrier
case MotionEvent.ACTION_DOWN:
if (x > barrier[0].getX() && x < barrier[0].getX() + 8
&& y > barrier[0].getX() && y < barrier[0].getY() + 8)
{
barrier[0].isTouched();
}
else if (x > barrier[1].getX() && x < barrier[1].getX() + 8
&& y > barrier[1].getX() && y < barrier[1].getY() + 8)
{
barrier[1].isTouched();
}
else if (x > barrier[2].getX() && x < barrier[2].getX() + 8
&& y > barrier[2].getX() && y < barrier[2].getY() + 8)
{
barrier[2].isTouched();
}
break;
// Touch-drag with the Barrier
case MotionEvent.ACTION_MOVE:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i] == barrier[0])
{
barrier[0].setX(x);
barrier[0].setY(y);
} // end if
else if (barrier[i] == barrier[1])
{
barrier[1].setX(x);
barrier[1].setY(y);
}
else if (barrier[i] == barrier[2])
{
barrier[2].setX(x);
barrier[2].setY(y);
} // end else if
} // end for
break;
case MotionEvent.ACTION_UP:
// Finger no longer on Barrier - Do Nothing
break;
}
return true;
}
// Render - Draws the Game Item Bitmaps to the screen
public void render(Canvas canvas)
{
// Set the background to white
canvas.drawColor(Color.WHITE);
barrier[0].draw(canvas);
barrier[1].draw(canvas);
barrier[2].draw(canvas);
}
// Update
// This is the Game's update method
// It iterates through all the Objects and calls their update() methods (if they have one)
public void update()
{
} // end update
In your barrier move code, you aren't checking a particular barrier is touched, so you're moving all of them to the same coordinates. The following loop does exactly the same thing as your current code:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
barrier[i].setX(x);
barrier[i].setY(y);
} //end for
To fix this, you need to check if the current barrier in the loop is the one that was touched, and can change that whole loop to something like:
// Move the Barrier the same as the finger
for (int i = 0; i < barrier.length; i++)
{
if (barrier[i].isTouched())
{
barrier[i].setX(x);
barrier[i].setY(y);
} // end if
} // end for
You would then need to make sure that you un-set the touched property in the ACTION_UP section. If you post your Barrier class definition I can probably help more.
I'm pretty sure your problem could be solved by making the barriers three separate objects instead of in an array.
As already pointed out, your code in handling the move is wrong because it moves all the Barriers to the same X,Y when it should only move the one that is touched.
Also, you are never resetting the isTouched on the objects in the Action up. When the user lifts their finger you should set them all to isTouched == false. If you don't do that then once you touch one it will always move to the X,Y.