I found this article: http://android-developers.blogspot.ca/2010/06/making-sense-of-multitouch.html which helped my understanding but I'm still not sure how to do what I'm trying to do.
In my game, I have a virtual analog stick and some buttons. Only 2 fingers will ever register at once. This is what I want. One for the analog stick and one for a button.
The main thing I'm unsure of is, say I put a finger down on the analog stick and move it around, then put a finger on the button, then release the button, the analog stick should keep moving to my first finger.
And vice versa, if the button touches first and then the analog stick, if I let go of the analog stick the button should still be pushed down.
Do touch pointers work in this fashion on Android, as in, once I put my finger down, regardless of any other fingers I put up or down, it will remember my first finger in order and give it a down, move move move up events?
Thanks
Ideally I wish I had a function like this:
void onTouch(int fingerID, int action, int x, int y)
{
}
Where each finger that is put down will receive down, move and up event when that finger goes up.
The game is a racing game so they need to be able to steer and push gas at the same time.
My problem is similar to this
identified multi touch pointer in action_move
Edit:
I have this code:
private void onTouch(int finger, int action, float x, float y)
{
if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN)
{
createInput(finger, x, y);
}
else if(action == MotionEvent.ACTION_MOVE)
{
inputMove(finger, x, y);
}
else if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP)
{
destroyInput(finger, x, y);
}
}
public void onTouch(MotionEvent ev)
{
final int pointerCount = ev.getPointerCount();
for (int p = 0; p < pointerCount; p++) {
onTouch(ev.getPointerId(p), ev.getAction(), ev.getX(p), ev.getY(p));
}
}
But it only works for the first one.
So from the examples given right in the MotionEvent class:
public boolean onTouch(MotionEvent ev) {
final int pointerCount = ev.getPointerCount();
for (int p = 0; p < pointerCount; p++) {
onTouch(ev.getPointerId(p), ev.getAction(), ev.getX(p), ev.getY(p));
}
return true;
}
You could take a look at the way the pinch is handled in AChartEngine for zoom. See this code, starting at line 80. It may look complex at the beginning, but it may help you on handling more use cases.
Related
Here is a link to a video I recorded of my issue: https://sendvid.com/rjpi6vnw
You'll notice that initially, I start at tile (0, 0) then after moving my character up and down multiple times, the screen will only go up to (1,0). So I lost a whole row of playable map. I only lose part of the map when my screen adjusts itself. You'll understand what I mean in a moment. I have a class called Player, and in it I have methods called moveRight(), moveLeft(), moveUp(), and moveDown(). I'm excluding all useless classes and methods in order to not waste your time. Here are my moveDown() and moveUp() methods:
public void moveUp(){
locY1 -= defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){ //locX1 and locY1 refers to the player's bounds location as set by setBounds()
Display.uni.moveMapDown(defaultMoveAmount); //Display.uni just means in the Display class
}
}
public void moveDown(){
locY1 += defaultMoveAmount;
if(viewShouldMoveVertically(locX1, locY1) == true){
Display.uni.moveMapUp(defaultMoveAmount); //defaultMoveAmount is the # of pixels the player moves each time the program updates
}
}
So I have KeyListeners that decide when these methods are called. The viewShouldMoveVertically() method is as follows:
public boolean viewShouldMoveVertically(int X1, int Y1){
if(Y1 < screenCenterY){ //screenCenterY is the number of vertical pixels on my screen/2
return false;
}
return true;
}
The moveMapUp() or moveMapDown() method is then called in the Display class:
int backgroundX1 = 0;
int backgroundY1 = 0;
public void moveMapUp(int moveAmt){
backgroundY1 -= moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
public void moveMapDown(int moveAmt){
backgroundY1 += moveAmt;
background.setBounds(backgroundX1, backgroundY1, backgroundX2 , backgroundY2);
}
So if you can't view the video at the link I posted, I'll describe the issue. When my character moves close to the edge of the map, I obviously wouldn't want the camera to show areas off of the map. So the camera stops, but my character may continue walking up to the border of the map. If my character is within 540 pixels of the top of the map, the camera won't move(I'm running on a 1920x1080 display). This is intended. When I move the character more than 540 pixels from the top of the map, the camera will now move with the player since he's in the center of the screen. But the issue is that IF and ONLY IF the camera ends up moving away from the top of the map, then I now lose exactly "defaultMoveAmount" pixels from the viewable area when I return to the top again. I can't seem to figure out how to fix this issue. Now, a little more you may end up wanting to know: I have the same issue moving horizontally as I have moving vertically. It is set up in the same way, so there was no point in making you guys read extra code. When viewing the video at the link, I have to click on the play button at the bottom left, or else it tries to make me add an extension to Chrome or something. The solution to my program's issue may end up being quite simple, but I just can't seem to figure it out. I ended up getting sick of it and decided getting a little help would be better than giving up for now. I am a beginner to programming, as I've only had 1 year of programming experience from an AP Compute Science class. I'm sure you may see a few things that seem dumb, and I welcome any suggestions or comments you may have, but please be aware that I am fairly new to this stuff. Primarily motion. And finally, I did not post a compile-able section of code due to things such as the graphics that are required. While I'm on here, if you have any suggestions or good references for figuring out whether a character is within an area in a large tile-like map, such as a door that can be opened, it would be appreciated.
I have solved the issue. Here is my updated viewShouldMoveVertically() method:
public boolean viewShouldMoveVertically(int X1, int Y1){
if( Y1 <= screenCenterY && previousY1 > screenCenterY ){ //if just touched highest camera point from below
return true;
}else if( Y1 >= mapPixelTotalY-screenCenterY && previousY1 < mapPixelTotalY-screenCenterY ){ //if just touched lowest camera point from above
return true;
} else if( Y1 <= screenCenterY){ //if just touched highest camera point from above or is just above
return false;
} else if( Y1 >= mapPixelTotalY-screenCenterY ){ //If touched lowest camera point from below or is just below
return false;
}
return true;
}
It turns out that when the player touches the "highest camera point from below" or the "highest camera point from above", different results are required. If touching the highest camera view from below, the camera still needs to move upwards just one more time. But when touching that same point from above, the camera shouldn't move, since the player was coming from an area that the camera wouldn't be vertically moving at.You can see my solution in the code. Also, I added a "previousY1" that records what the last Y1 value was, so that I can tell whether the player came from above or below.
I am making a bridge building game for Android. As you know there are two coordinates for drawing a line.
Firstly, When we push "put" button and select a dot then it's the first coordinate of the line, secondly the coordinates where we keep touching on screen is always displayed as the second coordinate, lastly, where we release our finger is decided as the second coordinate of the line. And there will be more than one lines.
I'd be glad if anyone explain these to me.
You will have to override the onTouchEvent function of the respective activity:
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
float x = event.getX();
float y = event.getY();
if (action == MotionEvent.ACTION_DOWN) {
// save the coordinates somewhere
} else if (action == MotionEvent.ACTION_UP) {
// save the coordinates as well
} else if (action == MotionEvent.ACTION_MOVE) {
// display the coordinates
}
}
Then you simply have to use the stored coordinates to draw a line between the coordinates, e.g. within a canvas that is located on your activity.
You will find a sophisticated example here: http://www.vogella.com/tutorials/AndroidTouch/article.html
I'm making a game using Libgdx, I need to know if the user is using two fingers and if they are placed in the correct position. One finger should be on the right side of the screen and the other in the left side of the screen.
the easiest solution without using any listeners is to just iterate over some count of pointers and calling simple Gdx.input.isTouched() - you have to set some "maximum pointers count" but hey - peoples usually has only 20 fingers :)
final int MAX_NUMBER_OF_POINTERS = 20;
int pointers = 0;
for(int i = 0; i < MAX_NUMBER_OF_POINTERS; i++)
{
if( Gdx.input.isTouched(i) ) pointers++;
}
System.out.println( pointers );
due to reference:
Whether the screen is currently touched by the pointer with the given index. Pointers are indexed from 0 to n. The pointer id identifies the order in which the fingers went down on the screen, e.g. 0 is the first finger, 1 is the second and so on. When two fingers are touched down and the first one is lifted the second one keeps its index. If another finger is placed on the touch screen the first free index will be used.
you can also easily the position of touching pointer by using Gdx.input.getX() and Gdx.input.getY() like
final int MAX_NUMBER_OF_POINTERS = 20;
int pointers = 0;
for(int i = 0; i < MAX_NUMBER_OF_POINTERS; i++)
{
if( Gdx.input.isTouched(i) )
{
x = Gdx.input.getX(i);
y = Gdx.input.getY(i)
}
}
and then you can for example put it into array
How to detect number of fingers being used?
You can do it with MotionEvent with getPointerCount()
You can detect how many fingers are on the screen doing this :
int PointerCount = event.getPointerCount();
I need to know if the user is using two fingers and if they are placed in the correct position
You can get the X,Y and compare it.
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = event.getX();
int y = event.getY();
return true;
}
For more information you can check MotionEvent Documentation so you can get there what you want.
Both GestureListener and InputProcessor have the touchDown method. This has the x and y value for each finger on the screen (pointer). Implement either one of these and you can override the touchdown to suit your needs. This is a great tutorial to start with. Hope this helps.
I am working with images only and the dimensions of the window that I am using to view my application may be different on different systems. I have a mouse action listener that is listening for clicks on the main view of my program. I have a rounded rectangle that looks like a button. I want to make it so that way the mouse action listener only listens to the area of the rounded rectangle rather than the entire image on all systems. Like the title says, not the entire image has content, in particular, the corners don't look like they are part of the image, so I don't want to allow the user to be able to click on parts of the image without content and get the same result as if they clicked on the part with content.
My image looks similar to this
(source: youthedesigner.com)
So I only want the program to do something if the user clicks on the button inside the image rather than the nice stuff around the button.
This is what I have right now to listen to clicks:
#Override
public void mouseClicked(MouseEvent e) {
for(int i = 0; i <= 200; i++) {
if(e.getY() >= 100+i && e.getY() <= 300) {
if(e.getX() >= 10+100-Math.pow(10000-(Math.pow((i-100),2.0)),.5)) && e.getX() <= 10+400-Math.pow(10000-(Math.pow((i-100),2.0)),.5))) {
// do stuff
i = 201;
}
}
}
}
The math equation I am using in my code looks like 110-(10000-(y-100)^2)^(1/2)), which, if graphed, would look like an open parenthesis, and 410+(10000-(y-100)^2)^(1/2)), which would look like a close parenthesis 400 units away from the first graph.
The code works fine on my system, but on other systems, it doesn't work at all and I am curious how I could move the location I am listening to to correspond to how the image is scaled.
Thank you very much for any help you can provide.
The for-loop is superfluous.
You could ensure that pixels outside the button (.png) have some transparency, and then check for the alpha color component.
In this case you could add a Rect and look for that:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = buttonRect.height() / 2;
if (mousePt.x < r) {
// Left circle with O at (r, r)
int xFromO = r - mousePt.x;
int yFromO = r - mousePt.y;
if (xFromO * xFromO + yFromO * yFromO > r * r) {
return false; // Outside circle
}
}
if (mousePt.x > buttonRect.right - r) {
// Right circle:
...
}
return true;
}
return false;
}
So, I used Joop's answer to solve my problem. His answer wasn't quite what I was looking for, but it gave me the idea I needed to solve my problem. The solution I came to was:
private boolean insideButton(Rectangle buttonRect, Point mousePt) {
if (buttonRect.contains(mousePt)) {
int r = (int)buttonRect.getHeight()/2; // radius of either of the circles that make up the sides of the rectangle
if(mousePt.x <= buttonRect.getWidth()/2) { // if it is on the left of the button
Point center = new Point((int)buttonRect.getX()+r, (int)buttonRect.getY()+r); // the center of the circle on the left
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2); // length from center to the point that the user clicked at
if(lengthToPoint > r && mousePt.x < center.x) { // if it is to the left of the center and out of the circle
return false;
} else {
return true;
}
} else { // if it is on the right, the rest of the code is just about the same as the left circle
Point center = new Point((int)buttonRect.getWidth()-r, (int)buttonRect.getY()+r);
double lengthToPoint = Math.pow(Math.pow(mousePt.x-center.x, 2)+Math.pow(mousePt.y-center.y, 2), 1.0/2);
if(lengthToPoint > r && mousePt.x > center.x) {
return false;
} else {
return true;
}
}
} else {
return false;
}
}
I know it is goes a little overboard with calculations and inefficient, but I wanted to present it this way to show a better idea of how my solution works.
I can think of at least two ways.
The first is to produce a mask image (black and white), where (for example) white would indicate the clickable area. Basically, you could compare the pixel color of the mask based in click pick point of the original image.
The other way would be to build a image map, basically using something like a Shape API to allow for non-rectangular shapes. This would allow to use Shape#contains to determine if the mouse clicked inside it or not
In either case, you need to take into account the x/y position of the original image
I am working on a game in which you are a simple circle that fires bullets and its multiplayer and so on. Well, I am trying to make boundaries sort of like a maze type thing that u have to go through I have tried collision detection like this:
public void checkCollisions(){
Rectangle r1 = bo.getBounds();
Rectangle d = p.getBounds();
if (d.intersects(r1))
border = true;
}
And basically if border = true then i stop the character from moving. I have 2 problems when i do this,
He doesnt stop just goes REALLY slow.
He stays at the REALLY slow state even off the border.
I use border like this:
boolean border = false;
then in my paint method i state this:
if (border)
p.dx = 0;
p.dy = 0;
p represents the Guy class :P
More of the dx and dy:
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if (key == KeyEvent.VK_A)
dx = -2;
if (key == KeyEvent.VK_D)
dx = 2;
if (key == KeyEvent.VK_W)
dy = -2;
if (key == KeyEvent.VK_S)
dy = 2;
and for keyReleased i just change the value of dx and dy to 0
also for how the guy moves:
public void move() {
x = x + dx;
y = y + dy;
}
Please help me figure out why this isn't working.
OK, I still think a full restructuring of your game logic is in order, but I think I can shed light as to what's going on. Let's look at the various places where things are happening:
PAINT: On the Swing thread, when paint() is called, you see if there were collisions and if so zero out the speeds (assuming you fix that if block).
KEY: On the Swing thread, when a key is pressed, you set the speed according to the key pressed.
CHECK: At some unknown point, you check for collisions and record whether there was one.
MOVE: At some unknown point, you update your "guy's" position with the speed.
So here's the problem: in Java, just like any other program, you get multiple key pressed events when you're holding down a key. There will be a short delay between the first and second, and then they will repeat rapidly. Try it in a text box in your browser, the same behaviour occurs there.
So how does that affect you? Well, you're probably getting into a scenario like this:
PAINT -> speed set to zero
KEY -> speed set back to -2
MOVE -> guy is moved -2
CHECK -> border = false
PAINT -> speed set to zero again
Really, if you restructure the code so that you get a game loop that looks something like this:
public void runGame() {
while(true) {
updateSpeeds();
updatePositionFromSpeed();
repaint();
}
}
Where updateSpeeds() would instead query whether the key is down or up and also compute whether the guy could move in that direction, and updatePositionFromSpeed() would update the guy's position. Then paint() would rely only on the guy's x and y coordinates, would not write to them, and would not need to know about the speed.
here's a very easy solution.
Here's a bit of my pseudo code.
if(player.getBounds().intersects(wall.getBounds())){
//Go Back to prior position, regardless of direction coming from. Since the reverse velocity X and velocity Y directions are taken care off
x -= velX;
y -= velY;
//Then Stop at that prior position to make next move
velX = 0;
velY = 0;
}