I managed to load a tmx map now I would like to create the obstacle that the sprite can not move, I recovered the obstacle like this :
try {
final TMXLoader tmxLoader = new TMXLoader(this, this.mEngine.getTextureManager(), TextureOptions.BILINEAR_PREMULTIPLYALPHA, new ITMXTilePropertiesListener() {
#Override
public void onTMXTileWithPropertiesCreated(final TMXTiledMap pTMXTiledMap, final TMXLayer pTMXLayer, final TMXTile pTMXTile, final TMXProperties<TMXTileProperty> pTMXTileProperties) {
/* We are going to count the tiles that have the property "cactus=true" set. */
if(pTMXTileProperties.containsTMXProperty("obstacle", "true")) {
//TMXTiledMapExample.this.mCactusCount++;
//coffins[coffinPtr++] = pTMXTile.getTileRow() * 15 + pTMXTile.getTileColumn();
}
}
});
How do I handle collisions with obstacles so as to prevent the player from walking through the obstacle (i.e., like a wall)?
I believe what you're asking is how do you implement collision handling. To be clear: Collision detection is the step where you determine that something is colliding(overlapping) with something else. Collision handling is where you, say, move one of those things such that it is no longer overlapping. In this case, I'm assuming we're past the collision detection and on to collision handling because you're in a method called "onTMXTileWithPropertiesCreated," which I'm guessing means the player is on such a tile. So here's the idea, put very simply:
When, due to the movement of the player (or some other sprite) you detect that the sprite is colliding with a sprite that you would like to be impassable -- "real" in your terms, you're going to want to move the sprite back the distance that would prevent it from overlapping.
Doing this with rectangles is very simple. Doing it with other shapes gets a little more complicated. Because you're working with a TMX tile map, rectangles will probably work for now. Here's a basic example with rectangles.
public boolean adjustForObstacle(Rect obstacle) {
if (!obstacle.intersect(this.getCollisionRect())) return false;
// There's an intersection. We need to adjust now.
// Due to the way intersect() works, obstacle now represents the
// intersection rectangle.
if (obstacle.width() < obstacle.height()) {
// The intersection is smaller left/right so we'll push accordingly.
if (this.getCollisionRect().left < obstacle.left) {
// push left until clear.
this.setX(this.getX() - obstacle.width());
} else {
// push right until clear.
this.setX(this.getX() + obstacle.width());
}
} else {
if (this.getCollisionRect().top < obstacle.top) {
// push up until clear.
this.setY(this.getY() - obstacle.height());
} else {
// push down until clear.
this.setY(this.getY() + obstacle.height());
}
}
return true;
}
What this is doing is calculating the overlapping rectangle and moving the sprite along the smallest dimension of overlap by the amount that will make it no longer overlap. Since you're using AndEngine, you can make use of the collidesWith() method in IShape, which detects collisions more elegantly than the above approach.
since I use this
if(pTMXTileProperties.containsTMXProperty("obstacle", "true")) {
//TMXTiledMapExample.this.mCactusCount++;
//coffins[coffinPtr++] = pTMXTile.getTileRow() * 15 + pTMXTile.getTileColumn();
//initRacetrackBorders2();
// This is our "wall" layer. Create the boxes from it
final Rectangle rect = new Rectangle(pTMXTile.getTileX()+10, pTMXTile.getTileY(),14, 14);
final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
PhysicsFactory.createBoxBody(mPhysicsWorld, rect, BodyType.StaticBody, boxFixtureDef);
rect.setVisible(false);
mScene.attachChild(rect);
}
Have fun !
Related
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'm developing a simple game by andengine.
I have 10 balls which are moving randomly on screen.i'm importing the balls as picture in sprites.if they move at the same coordinate , they pass though their own insides.but i want: if they move at the same coodirnates ,they should change their directions.so they cannot pass through their insides.how can i do that?
private Runnable mStartCircle = new Runnable() {
public void run() {
int i = circleNumber++;
Scene scene = Level1Activity.this.mEngine.getScene();
float startY = -64.0f;
float startX = randomNumber.nextFloat()*(CAMERA_WIDTH-70.0f);
float a= randomNumber.nextFloat()*(CAMERA_WIDTH-70.0f);
circles[i] = new Sprite(startX, startY, textRegCircle[i]);
circles[i].registerEntityModifier(
(IEntityModifier) new SequenceEntityModifier (
new MoveModifier(10.0f, circles[i].getX(), a,
circles[i].getY(),CAMERA_HEIGHT+64.0f)));
}
scene.getLastChild().attachChild(circles[i]);
if (circleNumber < 10){
mHandler.postDelayed(mStartCircle,1000);
}
}
};
Each object(ball) requires a bounding box, or in your case a bounding circle, which is equal to the size of your sprite.
When the game updates and any balls position changes, you have to test for collisions.
Circle to circle collision testing is the simplest type to do.
if distance between (ball1.pos + ball2.pos) is less than (ball1.radius + ball2.radius) = collision.
You then handle the collision by reversing the velocities or calculating new momentums or something. (You also need to move the objects apart so they are no longer colliding)
Just apply a physical connector between balls:
so it will collide and bounce back.
final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0.1f, 0.5f, 0.5f);
final Body ballBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, circles[i],BodyType.DynamicBody, boxFixtureDef);
this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(circles[i], ballBody, true, true));
this.mScene.attachChild(circles[i]);
I have been trying to use a MouseJoint to move a piece wherever the user touches. But the piece, being affected by the joint, behaves strangely, never reaching the point. This is the code (x and y are already converted to 'physical' units):
MouseJointDef mj_def;
MouseJoint mj = null;
Body mj_gbody;
public void move(float x, float y)
{
if(mj == null)
{
BodyDef mgbd = new BodyDef();
mj_gbody = wrld.createBody(mgbd);
//
mj_def = new MouseJointDef();
mj_def.bodyA = mj_gbody;
mj_def.bodyB = body;
mj_def.collideConnected = true;
mj_def.maxForce = 20.0f * body.getMass();
//mj_def.target.set(x,y);
mj = (MouseJoint)wrld.createJoint(mj_def);
body.setAwake(true);
}
mj.setTarget(new Vector2(x, y));
}
I was looking for some way to establish the anchor point in the BodyB, as the 'strange behaviour' that I mentioned seems to make the body gravitate around the established point (an orbit twice the width of the object), as if the anchor point was outside of the body (hexagon shaped, btw). But I don't see any way of doing so in libgdx.
Does anybody know what I am doing wrong? Thank you in advance!
Well, MouseJoint was working properly, I just misunderstood how MouseJoint works.
As it is clearly seen in the Box2d testbed, MouseJoint is used for dragging after selecting an object. Therefore, the anchor is assigned in the first target.set.
As I wanted to move the center of the object to the place where the mouse was (or the user touched), a mj_def.target.set(body.getPosition().x + 2.0f, body.getPosition().y + 1.0f); (the object is 4.0f by 2.0f) in the initialization solved the problem. Also, it may be not the best Joint for my intentions (to move an specific object to one place in the screen).
I had a small question.If i want to make a man run in android one way of doing this is to get images of the man in different position and display them at different positions.But often,this does not work very well and it appears as two different images are being drawn.Is there any other way through which i can implement custom animation.(Like create a custom image and telling one of the parts of this image to move).
The way i do it is to use sprite sheets for example (Not my graphics!):
You can then use a class like this to handle your animation:
public class AnimSpriteClass {
private Bitmap mAnimation;
private int mXPos;
private int mYPos;
private Rect mSRectangle;
private int mFPS;
private int mNoOfFrames;
private int mCurrentFrame;
private long mFrameTimer;
private int mSpriteHeight;
private int mSpriteWidth;
public AnimSpriteClass() {
mSRectangle = new Rect(0,0,0,0);
mFrameTimer =0;
mCurrentFrame =0;
mXPos = 80;
mYPos = 200;
}
public void Initalise(Bitmap theBitmap, int Height, int Width, int theFPS, int theFrameCount) {
mAnimation = theBitmap;
mSpriteHeight = Height;
mSpriteWidth = Width;
mSRectangle.top = 0;
mSRectangle.bottom = mSpriteHeight;
mSRectangle.left = 0;
mSRectangle.right = mSpriteWidth;
mFPS = 1000 /theFPS;
mNoOfFrames = theFrameCount;
}
public void Update(long GameTime) {
if(GameTime > mFrameTimer + mFPS ) {
mFrameTimer = GameTime;
mCurrentFrame +=1;
if(mCurrentFrame >= mNoOfFrames) {
mCurrentFrame = 0;
}
}
mSRectangle.left = mCurrentFrame * mSpriteWidth;
mSRectangle.right = mSRectangle.left + mSpriteWidth;
}
public void draw(Canvas canvas) {
Rect dest = new Rect(getXPos(), getYPos(), getXPos() + mSpriteWidth,
getYPos() + mSpriteHeight);
canvas.drawBitmap(mAnimation, mSRectangle, dest, null);
}
mAnimation - This is will hold the actual bitmap containing the animation.
mXPos/mYPos - These hold the X and Y screen coordinates for where we want the sprite to be on the screen. These refer to the top left hand corner of the image.
mSRectangle - This is the source rectangle variable and controls which part of the image we are rendering for each frame.
mFPS - This is the number of frames we wish to show per second. 15-20 FPS is enough to fool the human eye into thinking that a still image is moving. However on a mobile platform it’s unlikely you will have enough memory 3 – 10 FPS which is fine for most needs.
mNoOfFrames -This is simply the number of frames in the sprite sheet we are animating.
mCurrentFrame - We need to keep track of the current frame we are rendering so we can move to the next one in order.~
mFrameTimer - This controls how long between frames.
mSpriteHeight/mSpriteWidth -These contain the height and width of an Individual Frame not the entire bitmap and are used to calculate the size of the source rectangle.
Now in order to use this class you have to add a few things to your graphics thread. First declare a new variable of your class and then it can be initialised in the constructor as below.
Animation = new OurAnimatedSpriteClass();
Animation.Initalise(Bitmap.decodeResource(res, R.drawable.stick_man), 62, 39, 20, 20);
In order to pass the value of the bitmap you first have to use the Bitmap Factory class to decode the resource. It decodes a bitmap from your resources folder and allows it to be passed as a variable. The rest of the values depend on your bitmap image.
In order to be able to time the frames correctly you first need to add a Game timer to the game code. You do this by first adding a variable to store the time as show below.
private long mTimer;
We now need this timer to be updated with the correct time every frame so we need to add a line to the run function to do this.
public void run() {
while (mRun) {
Canvas c = null;
mTimer = System.currentTimeMillis(); /////This line updates timer
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
Animation.update(mTimer);
doDraw(c);
}....
then you just have to add Animation.draw(canvas); your Draw function and the animation will draw the current frame in the right place.
When you describe : " one way of doing this is to get images of the man in different position and display them at different positions", this is indeed not only a programming technique to render animation but a general principle that is applied in every form of animation : it applies to making movies, making comics, computer gaming, etc, etc.
Our eyes see at the frequency of 24 images per second. Above 12 frames per second, your brain gets the feeling of real, fluid, movement.
So, yes, this is the way, if you got the feeling movement is not fuild, then you have to increase frame rate. But that works.
Moving only one part of an image is not appropriate for a small sprite representing a man running. Nevertheless, keep this idea in mind for later, when you will be more at ease with animation programming, you will see that this applies to bigger areas that are not entirely drawn at every frame in order to decresase the number of computations needed to "make a frame". Some parts of a whole screen are not "recomputed" every time, this technique is called double buffer and you should soon be introduced to it when making games.
But for now, you should start by making your man run, replacing quickly one picture by another. If movement is not fuild either increase frame rate (optimize your program) or choose images that are closer to each other.
Regards,
Stéphane
So I have a bitmap that I have loaded from a resource file (an PNG image):
Bitmap map = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
If I draw this bitmap only once using canvas.drawBitmap(...); then there is no problem. However, If I draw that very same bitmap multiple times, then the picture keeps flashing back and forth, not steady like before.
I suspected that I cannot use the same bitmap more than once so I tried to load the image into a new bitmap every time when I want to draw the same picture, but it does not help, the behavior still persists.
The program is complicated, but basically, I want to draw a ocean wave. I have a image of a small wave. To make the effect of the wave moving from the left edge of the screen to the right edge. I keep track of the position of the left edge of the bitmap.
// The ocean.
private ArrayList<Wave> waves;
// Draw the waves and update their positions.
for (int i = 0; i < this.waves.size(); i++)
{
Wave wave = this.waves.get(i);
// Go through each of the sub-waves of this current wave.
for (int j = 0; j < wave.getSubWaveEdges().size(); j++)
{
// Get the sub wave.
final float subWaveEdge = wave.getSubWaveEdges().get(j);
canvas.drawBitmap( wave.getSubWave(j), subWaveEdge, 40, brush);
wave.setSubWaveEdge(j, subWaveEdge + (float) 0.5);
}
// Update this current wave.
wave.update();
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
}
If the left edge of a bitmap is inside the screen then I create a new bitmap from the same image file and draw. Here is the class Wave:
private class Wave
{
private Bitmap wave;
private float farthestEdge;
private ArrayList<Float> subWaveEdges;
private ArrayList<Bitmap> subWaves;
public Wave(Bitmap wave)
{
this.wave = wave;
this.farthestEdge = 0;
this.subWaveEdges = new ArrayList<Float>();
this.subWaves = new ArrayList<Bitmap>();
}
public Bitmap getWave ()
{ return this.wave; }
public void setWave (Bitmap wave)
{ this.wave = wave; }
public float getFarthestEdge ()
{ return this.farthestEdge; }
public void setFarthestEdge (final float furthestEdge)
{ this.farthestEdge = furthestEdge; }
public ArrayList<Float> getSubWaveEdges ()
{ return subWaveEdges; }
public void setSubWaveEdge (final int index, final float value)
{
this.subWaveEdges.remove(index);
this.subWaveEdges.add(value);
}
public void addSubWaveEdges (final float edge)
{
this.subWaveEdges.add(edge);
Bitmap newSubWave = BitmapFactory.decodeResource(getResources(), R.drawable.wave);
newSubWave = Bitmap.createScaledBitmap(newSubWave, MasterView.this.getWidth(), newSubWave.getHeight(), true);
this.subWaves.add(newSubWave);
}
public Bitmap getSubWave(final int index)
{ return this.subWaves.get(index); }
public void update ()
{
// Check to see if there is any sub-wave going outside of the screen.
// If there is then remove that wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) > MasterView.this.getWidth())
{
this.subWaveEdges.remove(index);
this.subWaves.remove(index);
}
// Set the farthest edge to the other side of the screen.
this.farthestEdge = MasterView.this.getWidth();
// Get the farthest edge of the wave.
for (int index = 0; index < this.subWaveEdges.size(); index++)
if (this.subWaveEdges.get(index) < this.farthestEdge)
this.farthestEdge = this.subWaveEdges.get(index);
}
}
Another suspicion that I have is that may be when I create two bitmaps from the same resource file, the pixels of the image are divided among two bitmaps, meaning that each bitmap only gets part of the pixels, not all. I am suspecting this because when the bitmaps are drawn, the parts where they overlaps are drawn steadily, no flashing.
Anyone has stumbled upon this problem and know how to fix?
Thanks,
Viktor Lannér, Thank you for helping, but I don't think that's the problem. I understand it is hard to read my codes since it is only a small piece of the big program.
However, I found the problem: This is not mentioned in my original question, but in order to simulate the two waves moving after one another, I have to draw the next wave as soon as the first wave enters the screen. However, each wave is longer than the width of the screen. Therefore, I have to draw the next wave from "outside" the screen if you know what I mean. It means that the next wave is drawn from a negative x-coordinate from outside the screen:
// If the wave has passed the left edge of the screen then add a new sub-wave.
if (wave.getFarthestEdge() >= 0)
wave.addSubWaveEdges(wave.getFarthestEdge() - this.getWidth());
And I found out that it does not like this. This is what causes the flashing back and forth.
In order to fix this, instead of drawing the next wave from outside the screen, I use this method:
canvas.drawBitmap (Bitmap bitmap, Rect source, Rect destination, Paint paint)
This method allows you to specify a rectangular region on the bitmap to be drawn to the screen and a rectangular region on the screen where that part of the bitmap will be drawn over. I use this method to draw the next wave. As the next wave moves into the screen, I change the "source" and "destination" appropriately to draw parts of the bitmap.
I just wanted to say that I had an issue where the images on my canvas were flashing back and forth, or, flashing between black and my first frame until I made a movement, almost as if the canvas was rapidly switching between its current and last image.
This might have had something to do with your situation, and to fix it I found out that it was because I was locking the canvas every frame, even when I had nothing to draw. For whatever reason, that lock, I think, created this situation.
I got around it by doing something like this:
if (needToRedraw == true) {
canvas = mSurfaceHolder.lockCanvas(null);
... logic to eventually draw on that canvas ...
}
Before canvas.drawBitmap(...) call; try to use canvas.drawColor(Color.BLACK) to clear the Canvas from previous drawings.
Sample code:
// Stuff.
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(wave.getSubWave(j), subWaveEdge, 40, brush);
// Stuff.