I have a code where i move an actor from point A to point B, i don't want it to stop on point B but to keep moving forward at the same direction exactly, i don't know how to do it.
also i want it to move in a fixed speed no matter what is the distance between the points, can someone help?
the action is:
Gdx.input.setInputProcessor(new InputAdapter() {
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
touchposx = screenX;
touchposy = screenY;
MoveToAction action = new MoveToAction();
action.setPosition(touchposx+300, screenHeight-touchposy+300);
action.setDuration(5f);
bullet.addAction(action);
return true;
}
});
}
You achieve this by using a combination of moveBy (for fixed speed), sequencing actions and a Remove Action.
But in my opinion, an Action is a too complicated approach to solve this. Actions are mainly designed for UI animations. Instead, I would override the bullet's act() method, use setPosition and remove the Actor from the stage when it left the screen.
Related
In my LibGDX game, I have a raster of clickable Actors. I want the player to be able to swipe across them and get the Actor where the swipe stops.
I don't want a GestureListener, the only thing I need is a touchUp event on the touchDown generated on another Actor. For example: think about Androids login screen where you swipe a pattern to unlock, but I only need an event for the last dot/Actor.
I have tried using touchDown / touchUp, but the event always fires on the Actor where the touchDown occurred.
A fine solution would be to get events for every Actor hit by the swipe, and then get a global touchUp event. Is this possible in a relatively accessible way - eg. without writing a whole new InputListener?
In touchUp() of your listener, call hit on the parent WidgetGroup of all your clickable Actors to see which one the finger is over when it is released. Note that since you're adding the actors to the group, the group needs to be added to the stage instead of adding the widgets to the stage directly. And remember that an Actor's X and Y are relative to its parent.
final WidgetGroup parent = new WidgetGroup();
InputListener inputListener = new InputListener(){
private final Vector2 tmp = new Vector2();
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
return true;
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
event.getTargetActor().localToParentCoordinates(tmp.set(x, y));
Actor releaseOverActor = parent.hit(tmp.x, tmp.y, true);
if (releaseOverActor != null){
//doSomethingTo(releaseOverActor);
}
}
}
for (Actor clickable : clickableActors){
parent.add(clickable);
clickable.addListener(inputListener);
}
The above is a little bit simplified. You might want to return true one touchDown only if pointer 0 is used, for example.
I have a libgdx game where I want to move a sprite when it is touched and dragged. If it comes close to other sprites I want to highlight them and I want to automatically slide into position if the sprite is released (touch up) and the sprite is close enough to another sprite.
So if I use actors the actors have to know about the other actors.
Is there already some mechanism to handle this in libgdx? Should I use something else than actors?
I could have a "manager" class which is called by the actors but I rather it would be the other way around, the "manager" knows about all the actors.
There's no easier mechanism in Scene2d than just iterating over actors and checking theirs position. If there are very many actors on the stage the operation can be expensive because of taking O(n2) (you have to check every actor with every others).
The good news is that you have to check only one actor with other which will make O(n) and is totally acceptable.
You also do not need any outer manager to check actors - just when actor is grabbed you should set it the flag and if the flag is true in the act actor's method you should check another actors - of course you need to create your own MyActor class extending Actor
class MyActor extends Actor
{
...
public boolean amIBeingDragged = false;
public void act(float delta)
{
super.act(delta);
if( amIBeingDragged )
{
Vector2 position = new Vector2(this.getX(), this.getY());
for(Actor actor : this.getStage().getActors())
{
if(actor == this) continue;
if(position.dst( new Vector2( actor.getX(), actor.getY() < thresholdDistance )
{
this.setPosition( actor.getX(), actor.getY() );
}
}
}
}
}
//drag's listener methods
public void dragStart(InputEvent event, float x, float y, int pointer)
{
( (MyActor) event.getTarget() ).amIBeingDragged = true;
}
public void dragStop(InputEvent event, float x, float y, int pointer)
{
( (MyActor) event.getTarget() ).amIBeingDragged = false;
}
...
There are of course geometric alghoritms to optimize case like this but in such simple usage I don't think it worths to deal with them.
If you want to try some alternative you can use Box2D (default Libgdx physics engine) collision system. The idea would be to give every actor a body and the body should have fixture bigger than actor. Then on collision you could handle the contact - but again if you aren't using box2d already it totally does not worth to implement it just for this.
Read more about Box2d collisions here.
I have implemented a ZoomViewGroup, which is capable of scrolling infinitely in all directions and also zooming infinitely while still delivering all touch events correctly offset to its child Views.
But when it comes to multi-touch, only the first pointer is offset correctly, and all others are pointing to a wrong location, and thats because there is the scaling factor I have to take care of. (As pointer 0 has a different offset from its original location than pointer 1 or 2, when scaling_factor != 1.0f)
I am saving my transformation in a Matrix, so it's easy to calculate the coordinates from screen to workspace and back using matrix.mapPoints(..) and the matrix inverse.
When I draw the child views, I can just apply the transformation matrix like this:
protected void dispatchDraw(Canvas canvas)
{
canvas.save();
canvas.concat(transformation_matrix);
super.dispatchDraw(canvas);
canvas.restore();
}
Same with touch events:
float[] touch_array = new float[2];
public boolean dispatchTouchEvent(MotionEvent event)
{
touch_array[0] = event.getX();
touch_array[1] = event.getY();
transformation_matrix.mapPoints(touch_array);
event.setLocation(touch_array[0], touch_array[1]);
return super.dispatchTouchEvent(event);
}
But MotionEvent.setLocation(float x, float y) does actually offset all pointers by the same amount. If we zoomed by 2.0f the offset is different for each pointer, so I have to be able to do something like MotionEvent.setLoction(int pointer_index, float x, float y) for each one individually. Is there anything I can do to achieve this?
EDIT: The only solution I know (and searched for) so far is to create a new MotionEvent with the new coordinates.
--
You can get all pointers (fingers touching) positions trough the methods getX(int pointerId)/getY(int).
You can get all on screen pointers trough the getPointerCount()
So to parse multi-touch you must do something like:
public boolean dispatchTouchEvent(MotionEvent event)
{
for(int i = 0; i < event.getPointerCount(); i++){
touch_array[0] = event.getX(i);
touch_array[1] = event.getY(i);
transformation_matrix.mapPoints(touch_array);
MotionEvent copy = MotionEvent.obtainNoHistory(event);
copy.setLocation(touch_array[0], touch_array[1]);
boolean handled = super.dispatchTouchEvent(copy);
copy.recycle();
if(handled) return true;
}
return false;
}
Also note that I created a copy of the MotionEvent object, so childrem can modify it without breaking the position trough the for.
Sorry, just now I noticed your last statement, you must copy the event to a new MotionEvent, so the childrem can parse itself and do its works, copying also will make your code safe of modifications, it's a bad idea to change the MotionEvent directly since it can break a for loop in any dispatch method.
I asked this at the libgdx forums but didn't get a response so I was hoping y'all could help me out:
I have Actors that represent game pieces. What I'm trying to do is make it so the player can click-and-drag the tile to move it around the screen and rotate it multiple times before submitting the placeTile command. From what I understand of DragAndDrop it doesn't seem to be designed with my use case in mind so I figured I'd instead attach a dragListener listener to each game piece (code below). It works well for dragging, except I can't figure out how to set the 'minimum distance before drag starts' to 0... but that's not my main question (though any insights would be appreciated )
Anyway, the big problem comes in when I rotate the actor, and then try to drag it: At 30 degrees rotation, drag acts almost like normal: at 60 degrees, very small movements of the mouse send the actor moving in a tight circle very quickly. Another 30 degrees, the tile actor exits the screen in 1-2 frames, moving in a wide arc. If the actor is rotated clockwise, it's movements are clockwise; same pattern for counter-clockwise.
It looks like the translation of the actor is taking rotation into account; I guess my question is, is it possible to rotate an Actor/Group without the rotation affecting future translations? Alternatively, is there a better way to drag an Actor around the screen based on touch/mouse input? I included some code below: I imagine I'm screwing up something basic, but I can't figure out what:
// during initial stage creation
tileActor.setOrigin(tileActor.getWidth() / 2, tileActor.getHeight() / 2);
tileActor.addListener(new DragListener() {
public void dragStart(InputEvent event, float x, float y,
int pointer) {
chosenTileActor = event.getTarget();
}
public void drag(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
target.translate(x, y);
}
});
And for the listener that deals with rotation via scrolling mouse wheel:
multiplexer.addProcessor(new InputAdapter() {
#Override
public boolean scrolled(int amt) {
if (chosenTileActor == null)
return false;
else
chosenTileActor.rotate(amt * 30);
return true;
}
});
Any pointers? Am I even going the right direction by using DragListener?
Thanks for reading!
Instead of translating, just set the actor's position directly to the stage coordinates of your drag event:
tileActor.addListener(new DragListener() {
private float offsetX, offsetY;
#Override
public void dragStart(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
this.offsetX = event.getStageX() - target.getX();
this.offsetY = event.getStageY() - target.getY();
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
event.getTarget().setPosition(event.getStageX() - offsetX, event.getStageY() - offsetY);
}
});
I'm computing the offsets in dragStart so that the actor doesn't immediately jump to wherever I clicked when I started dragging (making the drags relative to my mouse). Tested this and it works with any rotation.
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.