I’m trying to develop a very simple game for Android using libgdx: there is a bug that appears randomly on the screen and you have to touch it where the bug is to kill it. If you do, the game goes on until you miss three times, and then is game over. Simple enough.
So, I see the bug, I touch it right in the middle, but I miss: the touch point isn’t included in the bug rectangle, which is different from what I see on the screen. I print out the coordinates of both rectangle and point and I see that those of the rectangle were correct a couple of deltaTimes ago, but are no longer, despite of what I see on the screen. I have looked all over the place for a solution, read tutorials, libgdx documentation, but I don’t get it, I'm getting nuts.
This is the code for the touchDown event on the constructor
Gdx.input.setInputProcessor(new InputAdapter(){
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
unidadTiempo = unidadTiempo - aceleracion;
toque.set(screenX, screenY, 0f);
camara.unproject(toque);
if (bicho.contains(toque.x, toque.y)) {
aplastamiento.play();
bichosLiquidados++;
} else {
burla.play();
vidas--;
}
return true;
}
});
This is the method drawing the bugs and accelerating the bug appearing pace
void apareceBicho(){
ultimoBicho = TimeUtils.nanoTime();
juego.letra.draw(juego.batch, "Puntuacion: " + bichosLiquidados, 0, altoPantalla);
bicho.setX(xAleatorio);
bicho.setY(yAleatorio);
juego.batch.draw(bichoImagen, bicho.x, bicho.y);
while (TimeUtils.nanoTime() - ultimoBicho < unidadTiempo && vidas > 0){
unidadTiempo = unidadTiempo - aceleracion;
xAleatorio = MathUtils.random(randomx);
yAleatorio = MathUtils.random(randomy);
}
if(vidas == 0){
risa.play();
TextureRegion fotogramActual = animacionFinal.getKeyFrame(stateTime, true);
juego.batch.draw(fotogramActual, bicho.x, bicho.y);
juego.batch.flush();
gameover = true;
}
if (gameover){
juego.setScreen(new PantallaFinal(juego, this));
}
}
And lastly, the render method
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0.2f, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
camara.update();
juego.batch.setProjectionMatrix(camara.combined);
juego.batch.begin();
juego.batch.draw(yerbita, 0, 0, anchoPantalla, altoPantalla);
apareceBicho();
juego.batch.end();
}
This is what I meant by a couple of deltaTimes ago:
06-04 09:30:05.942 RECTANGLE POSITION AIMED TO: Posicion del bicho: 612.88776, 200.9191, 133.0, 126.0
06-04 09:30:07.950 ACTUAL RECTANGLE POSITION: Posicion del bicho: 426.92572, 111.10537, 133.0, 126.0
06-04 09:30:09.946 TOUCH POSITION: Coordenadas toque = 659.2, 250.3111
Possible reason :
bicho having zero width and height.
If you created rectangle by default constructor then need to be set width and hight of that rectangle.
bicho = new Rectangle();
bicho.setSize(bichoImagen.getWidth(),bichoImagen.getHeight());
Related
I'm trying to rotate the camera around a cube with LookAt() function and using the accelerometer in an Android device. It works well. But I want the rotation to stop at some value in the Y axis. Here is my code so far:
public Transform target; // The object to follow
public float topMargin = 0.2f; // Top rotation margin
// The position of the target
private Vector3 point;
void Start () {
point = target.transform.position;
transform.LookAt (point);
}
void Update () {
// Freeze
if (transform.rotation.y >= topMargin) {
transform.RotateAround (point, new Vector3 (0, 1, 0), 0);
}
// Freeze
else if (transform.rotation.y <= -topMargin) {
transform.RotateAround (point, new Vector3 (0, 1, 0), 0);
} else {
transform.RotateAround (point, new Vector3(0, 1, 0), Input.acceleration.x);
}
}
The problem is that when the camera reaches the top margin, I can't start rotating again in the opposite direction. I've tried with a flag variable but can't get the correct program logic (tried different if/else's). Any suggestion on how to achive this?
You could check for the (intended) rotation direction. If the result would lead to a rotation that is more appropriate (away from the boundary and towards the allowed area) you could "allow" the intended rotation to apply.
I guess using the accelerometer value to check for "intended direction" would be easiest and least error prone. (Rather than checking the rotation itself)
I have been experimenting with squeezing as much performance out of SurfaceView as possible. Currently, I'm subclassing it and implementing a runnable interface on it instead of a callback. I understand there is no hardware acceleration on it.
Still, if I either draw a canvas primitive vertical line scrolling across the screen or a bitmap vertical line, both run slower and slower after each pass. This felt to me like a memory leak, or is it just Android itself? Is OpenGL or another library really my last resort?
I've drawn plenty of scrolling backgrounds before at decent speeds (I think around 5 pixels per tick, this I'm aiming around 20-50 pixels a tick which if anything would be less stops along the way to render).
EDIT: Here is the SurfaceView extended, the thread it makes, the drawing method, and the initialization of it. Basically, this is in a slightly bigger class that just holds this screen's data. The drawXYZ() methods simply use the canvas primitives or a bitmap to paint mainly as the background, which is a solid background color with some vertical and horizontal lines on it like a music staff, little calculating is involved.
The drawCursor is what makes the scrolling vertical line and when I just let it loop the scrolling from left to right, it eventually lags much slower than the first scroll.
public class MySurfaceView extends SurfaceView implements Runnable
{
Thread renderThread = null;
SurfaceHolder holder;
volatile boolean running = false;
public MySurfaceView() {
super(mainActivity);
this.holder = getHolder();
holder.setFixedSize(screenW, screenH);
}
public void resume() {
running = true;
renderThread = new Thread(this);
renderThread.start();
}
#Override
public void run() {
while (running) {
if (!holder.getSurface().isValid()) {
continue;
}
Canvas canvas = holder.lockCanvas();
if(canvas != null) {
doDraw(canvas);
holder.unlockCanvasAndPost(canvas);
}
}
}
public void pause() {
running = false;
while (true) {
try {
renderThread.join();
break;
} catch (InterruptedException e) {
// retry
}
}
}
protected void doDraw(Canvas canvas)
{
canvas.drawColor(Color.rgb(56, 56, 62));
lastNotePlayed = OptionsContainer.getNotePlaying();
//Draw contours (rows).
paint.setColor(Color.rgb(0, 255, 255));
paint.setStrokeWidth(3);
paint.setTextSize(35);
drawContours(canvas, paint);
//Beats per measure (BPM).
paint.setColor(Color.rgb(233, 232, 232));
paint.setStrokeWidth(1);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(bpmLines);
drawBPM(canvas, paint);
paint.setPathEffect(null);
//Draw measures.
paint.setStrokeWidth(5);
drawMeasures(canvas, paint);
//Draw note node inputs.
paint.setColor(Color.rgb(76, 255, 0));
for (int i = 0; i < OptionsContainer.noteList.length; i++) {
if (OptionsContainer.noteList[i].getContour() != 0) {
if (OptionsContainer.noteList[i].getContour() > (OptionsContainer.contour / 2)) {
//Staff on left side, below note.
canvas.drawBitmap(lowerStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY(), null);
} else {
canvas.drawBitmap(higherStaffBmp, OptionsContainer.noteList[i].getX(), OptionsContainer.noteList[i].getY() - 40, null);
}
}
}
//Draw cursor.
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
drawCursor(canvas, paint);
if (OptionsContainer.isRest)
canvas.drawBitmap(restBmp, (OptionsContainer.screenWidth / 2), (screenHeight - 100) / 2, null);
}
}
#Override
public void init() {
surfaceView = new MySurfaceView();
surfaceView.setLayoutParams(layoutParams);
surfaceView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
// Normalize x,y between 0 and 1
float x = event.getX();
float y = event.getY();
if (x < (OptionsContainer.screenWidth) && y < screenH) {
NoteNode note = new NoteNode(x, y, MainActivity.options);
if (note.getContour() == OptionsContainer.noteList[note.getBeat() - 1].getContour()) {
OptionsContainer.noteList[note.getBeat() - 1] = new NoteNode(x, screenHeight + 200, MainActivity.options);
} else {
OptionsContainer.noteList[note.getBeat() - 1] = note;
}
}
}
return true;
}
});
mainActivity.addContentView(surfaceView, layoutParams);
surfaceView.resume();
}
EDIT #2: Final Answer
Add Path.reset() after the path is drawn in drawBPM(). I'd imagine that stops a memory leak of that path which is trying to keep track of ALL the paths it has been writing and overwriting, little to our knowledge just looking at the lines on the screen. There was a similar Stack Overflow question but fadden's debugging tips below were very helpful for initially trying to figure out what and where it was going wrong.
"Squeezing performance" and Canvas-rendering don't really go together on a SurfaceView, but you can do okay on many devices.
Grafika's "multi-surface test" Activity features a bouncing circle, rendered in software. I haven't noticed it get slower over time, so I suspect something is wrong in your code. Note Grafika does not subclass SurfaceView, and I generally recommend against doing so -- it's too easy to do the wrong thing. The only valid reason to subclass SurfaceView is if you want to draw on both the Surface and the View, e.g. for some sort of mask effect.
You didn't show any code, so there's not much more we can tell you.
I don't see anything blatantly wrong in the code; seems pretty straightforward. I'd check to make sure OptionsContainer.noteList.length isn't growing without bound. Next step would be to use traceview to figure out which part of the rendering is slow, or just spread System.nanoTime() calls around to identify which part is getting progressively slower. If everything in the method shown is executing at a consistent speed except drawCursor(), move the time-check calls into there, narrowing it down until you find what's draining your performance.
If something is consuming memory quickly enough to cause heap issues, you should see a great deal of GC activity in the logcat output. The DDMS allocation tracker tool can help with that.
I am developing a game in AndEngine and I want to attach a sprite to a parallax background (on my main menu) BUT I don't want the sprite to be repeated (which is what is currently happening).
I have tried this (below) which works but I use the sprites in the game so when I come back to the main menu, the sprites will have moved (I tried resetting the sprites but doesn't seem to be working).
Sprite playerCar = new Sprite(playerX, playerY,
mResourceManager.mPlayerCarTextureRegion,
mVertexBufferObjectManager);
playerCar.setRotation(-15);
attachChild(playerCar);
What I want to do is the following:
Define my sprite as normal:
Sprite playerCar = new Sprite(playerX, playerY,
mResourceManager.mPlayerCarTextureRegion,
mVertexBufferObjectManager);
playerCar.setRotation(-15);
Then attach it to my background:
ParallaxBackground menuParallaxBackground = new ParallaxBackground(0,
0, 0);
menuParallaxBackground.attachParallaxEntity(new ParallaxEntity(0,
new Sprite(0, SCREEN_HEIGHT
- mResourceManager.mParallaxLayerRoad.getHeight(),
mResourceManager.mParallaxLayerRoad,
mVertexBufferObjectManager)));
menuParallaxBackground.attachParallaxEntity(new ParallaxEntity(0,
playerCar));
Which also works but the car keeps on repeating which I do not want.
Any help would be appreciated! Thanks.
Problem was because of the onDraw method on the ParallaxEntity class in the ParallaxBackground class. There was a loop around where the sprites get drawn that keeps going until the sprites fill up the width of the screen. I simply created a custom class and removed the while loop so I could use it for my Main Menu.
ParallaxEntity onDraw code before:
public void onDraw(final GLState pGLState, final Camera pCamera, final float pParallaxValue) {
pGLState.pushModelViewGLMatrix();
{
final float cameraWidth = pCamera.getWidth();
final float shapeWidthScaled = this.mAreaShape.getWidthScaled();
float baseOffset = (pParallaxValue * this.mParallaxFactor) % shapeWidthScaled;
while(baseOffset > 0) {
baseOffset -= shapeWidthScaled;
}
pGLState.translateModelViewGLMatrixf(baseOffset, 0, 0);
float currentMaxX = baseOffset;
do {
this.mAreaShape.onDraw(pGLState, pCamera);
pGLState.translateModelViewGLMatrixf(shapeWidthScaled, 0, 0);
currentMaxX += shapeWidthScaled;
} while(currentMaxX < cameraWidth);
}
pGLState.popModelViewGLMatrix();
}
my CustomParallaxEntity onDraw code after (notice the last while loop removed):
public void onDraw(final GLState pGLState, final Camera pCamera,
final float pParallaxValue) {
pGLState.pushModelViewGLMatrix();
{
final float shapeWidthScaled = this.mAreaShape.getWidthScaled();
float baseOffset = (pParallaxValue * this.mParallaxFactor)
% shapeWidthScaled;
while (baseOffset > 0) {
baseOffset -= shapeWidthScaled;
}
pGLState.translateModelViewGLMatrixf(baseOffset, 0, 0);
this.mAreaShape.onDraw(pGLState, pCamera);
pGLState.translateModelViewGLMatrixf(shapeWidthScaled, 0, 0);
}
pGLState.popModelViewGLMatrix();
}
Thanks to the comments on my question for helping me figure out a solution.
Evening Everyone,
I am attempting to get familiar with libdgx and android by going thru the tutorial Here. All seems good except for grabbing the screen coordinates as they get skewed in a Vector3 conversion.
So x input of 101 gets converted to -796, y input of 968 converted to -429 (touching the upper left corner of the screen, same results from emulator as from my phone). When clicking the bottom right corner, the animation fires in the middle of the screen.
It all seems pretty basic so not really sure what I am setting incorrectly to get a skewed conversion. Any help is appreciated!
camera creation:
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(camera.viewportWidth * .5f, camera.viewportHeight * .5f, 0f);
Grabbing touch coord:
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
touchCoordinateX = screenX;
touchCoordinateY = screenY;
stateTime = 0;
explosionHappening = true;
return true;
}
Render loop:
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
batch.begin();
if (!explosionAnimation.isAnimationFinished(stateTime) && explosionHappening) {
Vector3 touchPoint = new Vector3();
touchPoint.set(touchCoordinateX,touchCoordinateY,0);
TextureRegion currentFrame = explosionAnimation.getKeyFrame(stateTime, false); // #16
camera.unproject(touchPoint);
batch.draw(currentFrame, touchPoint.x, touchPoint.y);
}
// batch.draw(img, 0, 0);
batch.end();
if (explosionAnimation.isAnimationFinished(stateTime)){explosionHappening = false;}
}
I think you forgot to set camera projection matrix to your SpriteBatch. Just add
batch.setProjectionMatrix(camera.combined);
before
batch.begin();
Apparently this is a popular topic. Im a beginner, so my problem is probably something fairly trivial. This is a very simple game kind of like Pong. I am using this code to draw my game:
#Override
public void paint(float deltaTime) {
Graphics g = game.getGraphics();
// draw the game elements
if (state == GameState.Running){
g.drawImage(Assets.back, 0, 0);
//g.drawRect(0, 0, g.getWidth(), g.getHeight(), Color.BLACK);
g.saveCanvas();
g.drawTransRect(0, 0, scene.getLine(), g.getHeight());
g.drawImage(Assets.fore, 0, 0);
//g.drawRect(0, 0, g.getWidth(), g.getHeight(), Color.WHITE);
g.restoreCanvas();
for (Pieces p : pieces){
if (p.getType() == true)
g.drawImage(Assets.pos, p.getX(), p.getY());
if (p.getType() == false){
g.drawImage(Assets.neg, p.getX(), p.getY());
}
}
}
If I run the code as-is, the frame rate looks to be about 15FPS? However, use drawRect (commented out above) instead of the drawImage (background and foreground bmps as Assets), my FPS is at least 60. Im assuming this means that there is much more CPU power involved in displaying bmps vs Rects. How can I use my images and maintain a decent framerate?
Thanks.
EDIT:
My drawImage method looks like this, if it helps:
public void drawImage(Image Image, int x, int y) {
canvas.drawBitmap(((AndroidImage)Image).bitmap, x, y, null);
}