MenuItemImage get touch coordinates - cocos2d-x

I have a MenuItemImage when touched calls the registered callback.
void callback(Ref* pSender)
{
...
}
Is it possible from pSender to get the actual touch coordinates??
This way I can see if the touched pixel is transparent on the image or not.

Related

Handle friendly fire collision detection

I want to create a friendly-fire mechanism. Essentially, my cubes fire repeatedly a bullet to hit enemies. I also want to detect the hit between the bullet and another cube (friendly fire). In order to avoid that the bullet's boundaries overlap with the cube that fired it, in my "Cube" class i overrided equals. Each Cube has a unique id, initialized as soon as the Cube is created.
The issue is that i have a very bad bug. When i add the first Cube all behaves normally, but as soon as i add the second one, a collision is detected for both the cubes' bullets, instantly, even if they are not firing to each other. This happens even if i add more cubes. I checked the boundaries and they seems fine.
I put some code:
collision detection
// check collisions between bullets and cubes (friendly fire)
for(BaseBullet bullet : bullets) {
for(BaseSquare square : squares) {
// exclude collision between bullet and its own Cube
if (!bullet.getBaseSquare().equals(square)) {
// check if bounds overlap
if (square.getBounds().overlaps(bullet.getBounds())) {
System.out.println("collision between bullet and qube");
// propagate the event to anyone is interested in it
EventsManager.qubeHitByQube(bullet, square);
bullet.remove();
bulletsToDestroy.add(bullet);
}
}
}
}
cube's equals method
#Override
public boolean equals(Object o) {
if(!(o instanceof BaseSquare) || o == null) {
return false;
}
BaseSquare baseSquare = (BaseSquare)o;
return baseSquare.getUniqueID().equals(getUniqueID());
}
NOTE
With "friendly fire" i mean when your objects hit themselves and not the enemies.
I would suspect the problem is more likely coming from 'if (square.getBounds().overlaps(bullet.getBounds()))' than the square's equals() method.
Are 'BaseBullet' and 'BaseSquare' extending LibGDX's Sprite class? Should you not be calling getBoundingRectangle()?

MapControl differentiate between user or programmatic center change

In the WinRt/WP 8.1 MapControl, how do I differentiate between when the user changed the center of the screen by swiping vs a programmatic change?
The WinRt/WP 8.1 MapControl has a CenterChanged event ( http://msdn.microsoft.com/en-us/library/windows.ui.xaml.controls.maps.mapcontrol.centerchanged.aspx ), but this does not provide any info about what caused the center change.
Is there any other way of knowing whether or not the user changed the map center?
/* To give some more context, my specific scenario is as follows:
Given an app which shows a map, I want to track the gps position of a user.
If a gps position is found, I want to put a dot on the map and center the map to that point.
If a gps position change is found, I want to center the map to that point.
If the user changes the position of the map by touch/swipe, I no longer want to center the map when the gps position changes.
I could hack this by comparing gps position and center, but he gps position latLng is a different type & precision as the Map.Center latLng. I'd prefer a simpler, less hacky solution.
*/
I solved this by setting a bool ignoreNextViewportChanges to true before calling the awaitable TrySetViewAsync and reset it to false after the async action is done.
In the event handler, I immediately break the Routine then ignoreNextViewportChanges still is true.
So in the end, it looks like:
bool ignoreNextViewportChanges;
public void HandleMapCenterChanged() {
Map.CenterChanged += (sender, args) => {
if(ignoreNextViewportChanges)
return;
//if you came here, the user has changed the location
//store this information somewhere and skip SetCenter next time
}
}
public async void SetCenter(BasicGeoposition center) {
ignoreNextViewportChanges = true;
await Map.TrySetViewAsync(new Geopoint(Center));
ignoreNextViewportChanges = false;
}
If you have the case that SetCenter might be called twice in parallel (so that the last call of SetCenter is not yet finished, but SetCenter is called again), you might need to use a counter:
int viewportChangesInProgressCounter;
public void HandleMapCenterChanged() {
Map.CenterChanged += (sender, args) => {
if(viewportChangesInProgressCounter > 0)
return;
//if you came here, the user has changed the location
//store this information somewhere and skip SetCenter next time
}
}
public async void SetCenter(BasicGeoposition center) {
viewportChangesInProgressCounter++;
await Map.TrySetViewAsync(new Geopoint(Center));
viewportChangesInProgressCounter--;
}

GameStateManager LibGDX

I started a project with libgdx and I have a GameStateManager for my GameStates Menu and Play.
If I run the project it shows the Menu and then I can click a button to open the Play GameState.
The Problem is, that when I ended the Game it should show the Menu State again, but I get a black screen. I tested if the render() method is started (with System.out...) and the render() method in Menu is starting.
I am not shure why I get a black screen when I "reopen" the Menu state. Maybe its not working because I use Box2D in Play but I dont know.
Here some code:
This is the method in Play which should open the Menu if the player is at the end:
public void playerEnded() {
gsm.setState(GameStateManager.MENU);
}
Maybe you can tell me, if I have to end box2d things or so.
I hope someone can help me, and if you want more code - no problem.
Your custom GameStateManager should extend this class:
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/Game.html
To change screens you should be using Game.setScreen(Screen screen)
Each different screen should be an implementation of Screen.
So the way it works in my libGDX projects is such that GameScreen extends Screen, and MenuScreen extends Screen. That way I can change what draws on what screen.
This all goes back to interfaces and polymorphism, so if you don't get those concepts, just give it a quick google and you'll get an idea what you need to do.
Chances are that your are defining a
Stack<GameState> gameStates
in your GameStateManager to manage your GameStates.
If so, you could do some reading on using a Stack. Anyway, here's what could do to solve your problem:
I'm assuming you have the following structure in your GamestateManager;
public void setState(int state){
popState();
pushState(state);
}
public void pushState(int state){
gameStates.push(getState(state));
}
public void popState(){
GameState g = gameStates.pop();
g.dispose();
}
private GameState getState(int state){
if(state == MENU) return new Menu(this);
if(state == PLAY) return new Play(this);
return null;
}
When you click your start button in your Menu-GameState, you'll want to launch the Play-GameState.
Now, instead of using the setState method, which removes the top state (pop) and then pushes the new state. Try using only the pushState method. This will put your new Play-GameState on top of your Menu-GameState in the GameState-Stack.
When you're done playing, you can pop your Play-GameState and the Menu-GameState should reappear. But this time it won't instantiate a new Object, but reuse the one you used when you started the game.
// in the Menu-GameState:
public void startPlay() {
gsm.pushState(GameStateManager.PLAY);
}
// in the Play-GameState:
public void playerEnded() {
gsm.popState();
}
This won't affect gameplay performance as you would render and update your game with only the GameState that is on top of the Stack, like this:
public void update(float dt) {
gameStates.peek().update(dt);
}
public void render() {
gameStates.peek().render();
}
The peek method takes the GameState from the top of the Stack, but leaves it there.
The only downside is that the Menu-Gamestate would stay in-memory during your Play-State.
Also, if this method works, you could still do it your way, but you should check the way your Menu-GameState is instantiated and identify problems when it's instantiated twice.
I implemented a similar StageManager in my game. If you are using viewports/cameras in your game, then it is likely that when you go back to your menu state, the viewport is still set to the Game state's viewport.
For my app, I had my State class have an activate() method which would be called whenever a state became the active state:
public void activate(){
stage.getViewport().update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
}

Input detection for a Group with overlapping transparent Images

I use a Group to store some Images and draw them to a SpriteBatch. Now I want to detect which Image has been clicked. For this reason I add an InputListener to the Group to get an event on touch down. The incoming InputEvent got an method (getTarget) that returns an reference to the clicked Actor.
If I click on a transparent area of an Actor I want to ignore the incoming event. And if there is an Actor behind it I want to use this instead. I thought about something like this:
myGroup.addListener(new InputListener() {
#Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
Actor targetActor = event.getTarget();
// is the touched pixel transparent: return false
// else: use targetActor to handle the event and return true
};
});
Is this the right way to do it? I thought returning false for the method touchDown would continue propagation for the event and let me also receive touchDown events for other Actors at the same position. But this seems to be a misunderstanding...
UPDATE
P.T.s answer solves the problem of getting the right Event. Now I have got the problem to detect if the hit pixel is transparent. It seems to me that I need the Image as a Pixmap to get access. But I do not know how to convert an Image to a Pixmap. I also wonder if this is a good solution in terms of performance and memory usage..
I think you want to override the Actor.hit() method. See the scene2d Hit Detection wiki.
To do this, subclass Image, and put your hit specialization in there. Something like:
public Actor hit(float x, float y, boolean touchable) {
Actor result = super.hit(x, y, touchable);
if (result != null) { // x,y is within bounding box of this Actor
// Test if actor is really hit (do nothing) or it was missed (set result = null)
}
return result;
}
I believe you cannot accomplish this within the touchDown listener because the Stage will have skipped the Actors "behind" this one (only "parent" actors will get the touchDown event if you return false here).

as3: MOUSE_OVER is slow/not working correctly

Okay so I'm working in pure as3 (no creative suite or xml). I have a sprite. It draws a large rectangle and then a slightly smaller rectangle. I want to change the color of the slightly smaller rectangle when I hover the mouse over it. Right now though it will either not respond (I can plainly have the mouse over the rectangle and nothing happens) or it is slow to respond. In addition the collision area of the rectangle seems a bit off, ie it responds more frequently when I have the mouse on the upper left corner of the rectangle than when I have the mouse elsewhere on it.
Anyways here's the code I'm using:
public function MAIN()
{
BUTTON_1.graphics.clear();
BUTTON_1.graphics.beginFill(0x000000);
BUTTON_1.graphics.drawRect(188,96,104,24);
BUTTON_1.graphics.endFill();
BUTTON_1.graphics.beginFill(0x0000DC);
BUTTON_1.graphics.drawRect(190,98,100,20);
BUTTON_1.graphics.endFill();
addChild(BUTTON_1);
BUTTON_1.addEventListener(MouseEvent.MOUSE_OVER,MOUSE_OVER_1);
function MOUSE_OVER_1():void
{
removeChild(BUTTON_1);
BUTTON_1.graphics.clear();
BUTTON_1.graphics.beginFill(0x000000);
BUTTON_1.graphics.drawRect(188,96,104,24);
BUTTON_1.graphics.endFill();
BUTTON_1.graphics.beginFill(0x0000A0);
BUTTON_1.graphics.drawRect(190,98,100,20);
BUTTON_1.graphics.endFill();
addChild(BUTTON_1);
}
}
I'm pretty new to as3 so if there's a better way to do this tell me.
this code doesn't seem correct. listener for mouse events takes MouseEvent object as parameter. So your MOUSE_OVER_1 should be like function MOUSE_OVER_1(e:MouseEvent):void.
I ended up using hitTestPoint, like this:
if (BUTTON_1.hitTestPoint(mouseX,mouseY,true))
{
removeChild(BUTTON_1);
removeChild(TEXT_MENU_1);
BUTTON_1.graphics.clear();
BUTTON_1.graphics.beginFill(0x000000);
BUTTON_1.graphics.drawRect(188,96,104,24);
BUTTON_1.graphics.endFill();
BUTTON_1.graphics.beginFill(0x0000A0);
BUTTON_1.graphics.drawRect(190,98,100,20);
BUTTON_1.graphics.endFill();
addChild(BUTTON_1);
addChild(TEXT_MENU_1);
}
else
{
removeChild(BUTTON_1);
removeChild(TEXT_MENU_1);
BUTTON_1.graphics.clear();
BUTTON_1.graphics.beginFill(0x000000);
BUTTON_1.graphics.drawRect(188,96,104,24);
BUTTON_1.graphics.endFill();
BUTTON_1.graphics.beginFill(0x0000DC);
BUTTON_1.graphics.drawRect(190,98,100,20);
BUTTON_1.graphics.endFill();
addChild(BUTTON_1);
addChild(TEXT_MENU_1);
}