I am experimenting with 2D graphics in Android during my off time, and I'm intrigued by the idea of creating a sprite based "sandbox" for playing around. I've looked around online and found some good tutorials, but I'm stuck with a fairly basic problem: My sprite moves faster than the terrain and "glides" over it. Also, he slowly outpaces the terrain scrolling and moves to the edge of the view.
I've got an animated sprite that I want to move around the world. I do so by changing his absolute coordinates(setting X and Y position in an 'update()' function and applying a scalar multiple to speed up or slow down the rate at which he's moving around.
this.setXPos(currX + (speed * dx2));
this.setYPos(currY + (speed * dy2));
Underneath that sprite, I'm drawing "terrain" from a bitmap. I want to continue to move that sprite around via the coordinate accessors above, but also move my view and scroll the terrain so that the sprite stays in the same relative screen location but moves through the "world". I have an imperfect implementation where player is my sprite and field is my terrain:
#Override
protected void onDraw(Canvas canvas)
{
player.updateLocation(GameValues.speedScale);
field.update(new Point(player.getXPos(), player.getYPos()));
field.draw(canvas);
player.draw(canvas);
//more stuff here...
}
And field.update() looks like(Warning: Hard-coded scariness):
public void update(Point pt)
{
sourceRect.left = pt.x - 240;
sourceRect.right = pt.x + 240;
sourceRect.top = pt.y - 400;
sourceRect.bottom = pt.y + 400;
}
The thinking there was that I would eventually just get screen dimensions and make it less 'hack-y', but get something together quickly. This could easily be where my issue is coming from. Of immediate interest is field.draw():
#Override
public void draw(Canvas canvas)
{
try
{
canvas.drawBitmap(fieldSheet, sourceRect, destRect, null);
}
catch(Exception e)
{
//Handle Exception...
}
}
You'll notice I'm using the overload of drawBitmap() that accepts a source and destination Rect and that the field.update() method moves that sourceRect to match the movement of the sprite. How can I keep the "camera (so to speak)" centered on the sprite and scroll the terrain appropriately? I had thought that moving just the sourceRect and maintaining a constant destRect would do so, but now I'm thinking I have to move the destRect around the "world" with the sprite, while maintaining the same dimensions.
Is there a better (read functional) way of making this work? I'm coming here somewhat shamefully, since I think this should be somewhat easier than it seems to be for me. Any and all help or suggestions are appreciated. Thanks!
*Edit:*Does it make sense to also move the destination Rect at the same speed as the sprite? I think I conflated the source and destination Rect's and left the destRect immobile. I'll update with the results after I get a chance to try it (maybe during lunch).
*Edit_2:*Changing the destination rectangle didn't get me there, but using the View.scrollBy(x, y) method gets me close to totally satisfied. The remaining question to be satisfied is how to "clip" the View scrolling to a rectangle that represents the "field". I believe that the View.getLeft() and View.getTop() functions, offset by the screen width and height, can be used to specify a Rect that can be virtually moved around within the constraints of the "world" and block further deltas from being argued to the View.scrollBy() method. The reason I look toward this approach is because the View doesn't seem to be positioned in absolute space, and a View.getLeft() call, even after a View.scrollBy(x, y) where x > 0, returns a 0.
Related
I created a rectangle using x and y integers which change values when I touch the rectangle and swipe in a certain direction (x increases when I swipe right, y increases when I swipe up, etc.). This rectangle is also being rendered onto a canvas. I have a game loop which calls an update() method and render() method. The update() method updates the x and y values and creates a collision detection rectangle, the render() method then uses these values to render an updated rectangle to my canvas.
Say I use the method canvas.translate(0, -50) before I draw this rectangle in my game loop. The rectangle appears 50 pixels above where it used to be, however my x and y values remain the same so I have to swipe below my rendered rectangle for it to actually do something. What's a simple and efficient way of updating the x and y so that my rectangle is actually located where it is rendered and not below it? I need the collision detection box to be located where the rectangle is drawn.
I taught myself how to program, so I am not really sure if there is an easier way, but if I were you whenever you call canvas.translate() just change the x and y values to adjust. For example, this is what I would do for the sample you gave:
canvas.translate(0, -50)
x = x+0
y = y+(-50)
If you are too lazy to add the extra two lines you could put it all in to a function and just replace canvas.translate() with whatever function you made.
In fact I will make that function right now. Why not?
public void translate(double XMove, double YMove) {
canvas.translate(XMove, YMove);
x = x+XMove;
y = y+YMove;
}
I would like to detect collisions between shapes dynamically drawn on a canvas (SurfaceView) for an Android game.
I can easily use intersect method of Rect or RectF objects but the result is not very good (see picture below where I have a "false" detection).
I don't want to use Bitmap so it's impossible to use the "pixel perfect" method.
Do you know a way to do this for circle, rect, triangle and other basic shapes intersection ?
Thx for help ;)
For a good collision detection you have to create your own models behind. In those models you specify the conditions that two objects colide.
For example, a circle is described by the center position and by the radius. A square is described by the left down corner and by the edge length.
You don' t have to describe all possible poligons, you can use the so called bounding boxes, meaning that, for a complex random poligon you can use a square or whathever shape fits it best(also you can use multiple shapes for a single object).
After you have the objects in mind you compute the condition that each one of them will colide with all other shapes including itself.
In your example The sphere and the square colides if the distance between any corner of the square is greater than the circle's radius.
Here you can read more http://devmag.org.za/2009/04/13/basic-collision-detection-in-2d-part-1/
This problem can get very complex, keep it simple if you want something simple.
Here is a directly applicable method I use in my own game to detect circle and rectangle intersection. It takes the ball (which is a view in this case) and the rectangle (also a view) to be checked for collision with the ball as parameters. You can put the method in a Timer and set the interval you want the circle and rectangle to be checked for collision.
Here is the method:
public boolean intersects(BallView ball, Rectangle rect) {
boolean intersects = false;
if (ball.getX() + ball.getR() >= rect.getTheLeft() &&
ball.getX() - ball.getR() <= rect.getTheRight() &&
ball.getY() + ball.getR() <= rect.getTheBottom() &&
ball.getY() - ball.getR() >= rect.getTheTop())
{
intersects = true;
}
return intersects;
}
getR() gets the circle's radius
getX() gets the center of the circle's X position value
getTheLeft() gets the rectangle's left X value
getTheRight() gets the rectangle's right X value
getTheTop() gets the rectangle's top Y value
getTheBottom() gets the rectangle's bottom Y value
If you can't directly use this method in your code you can still conjecture the logic it entails to implement it where it would work for you. It detects all collisions without using pseudo-collision detection like a collision box for the circle.
Good luck! And if you have any questions feel free to ask, I'm here to help!
To know if a polygon in 2d is colliding with a circle, you can test, for each of its lines, where is the point on the line that is closest to the center of the circle (this might help).
Then, check if the point you found is between to two corners that make the line - that is, that the point is actually on the line, and not just on its continuation - and if the distance of that point to the center of the circle is smaller or equal to the radius of the circle. If both are true for any of the lines of the polygon, you have a collusion. You also have to check for the edge cases where the corners of the polygon might be in, or touching the circle.
For two circles, this is easier. Check the distance between the centers, and compare it to the sum of their radiuses. If the distance is smaller or equal to the sum, you have a collusion.
Im doing a game and I'm using SurfaceView. In the game I have 25 small dots that are moving around. The goal is to make a game where the player draw a circle around some of the dots with the same color. My problem is that while I draw this circle and after I have lift my finger from the screen, all the dots are moving very, very slow! I guess the reason for this is all the line segmets that are being drawn constantly together with all the dots.
Is it possible to have the same moving speed of the dots all the time? I tested with SystemClock.Sleep(1) but it didn't helped much.
// Method to draw objects
private void drawObjects(Canvas canvas) {
SystemClock.sleep(1);
synchronized (this) {
// Clear screen with black color
canvas.drawRGB(0, 0, 0);
// Draw line
if(startDrawLine) {
// Set properties to Paint object
paint.setColor(Color.WHITE);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
// Draw path
path.moveTo(touchDownX, touchDownY);
for(Point point: points) {
path.lineTo(point.x, point.y);
canvas.drawPath(path, paint);
}
path.reset();
}
}
// Call method draw() in CircleManager to draw all circles in list
circleManager.drawCirclesInList(canvas);
}
Slow drawing is caused by to many points in your path.
If you are handling Touch event, you can expect hundreds and thousands of touches (your points) in short period of time.
You need to interpolate your points somehow - take averages, ignore same and close points or something else.
When you animate the "dots" your ANIMATION MUST BE BASED ON TIME, and not on the actual speed of the drawing on hardware.
To base your animation on time, you should calculate time passed since previous frame and use this time in function which calculates new position of the dot.
Here is a great article on the subject:
http://www.koonsolo.com/news/dewitters-gameloop/
EDIT - Response to a comment:
Suppose that your "dot" (I will call it a ball) needs to move horizontally (by X) from left to right at a constant speed of 100 units per second.
If you are doing your calculation WITH ASSUMPTION that your game will be running at 25 FPS, you simply add 4 to X on each frame.
The problem with this approach is that getting constant FPS is very hard to achieve.
On the fast hardware you will be sacrificing smoothness by limiting FPS,
on slow hardware you will be forced to skip drawing (drop) some frames (which can be jumpy with constant FPS).
Timer in Android is not very precise and also thread sleep functions are inaccurate.
In your case, instead of trying to force the constant FPS or calculate current FPS, try to wrap your head around and rethink of your problem
in a context of time.
When you think about time, it does not matter how fast is the game
running (how many FPS) and what happened before.
You just need to ask a question "Where the ball should be in this
moment, right now?"
If you, for example, know that ball movement stared at origin position X0 at origin time T0 then at the current moment TC ball position should be X = (TC - T0) * 100
Second approach is to measure time passed TD since last ball position update. Then the ball position should be updated like this: X = X + TD * 100
Once you get used to this approach, you will see that vast majority of animations is really trivial to implement with a great level of accuracy.
I have a canvas that I am using a ScaleGestureDetector to zoom in my android app. Here is the code I have so far:
//SCALING --------------------------------------------------
//get center of the viewport
int centerX = xLoc+((int)(screenWidth/2*scaleFactor));
int centerY = yLoc+((int)(screenHeight/2*scaleFactor));
scaleFactor /= detector.getScaleFactor();
// Don't let the object get too small or too large.
scaleFactor = Math.max(1.0f, Math.min(scaleFactor, maxScaleFactor));
//Make sure the viewport is repositioned
xLoc = centerX-((int)(screenWidth/2*scaleFactor));
yLoc = centerY-((int)(screenHeight/2*scaleFactor));
//-----------------------------------------------------------
This works great to zoom in and out of my canvas. The xLoc and yLoc represent the top left corner of my viewport in relation to the entire image I am drawing a portion of. The problem I have with this code though, is that It zooms in to the center of the viewport. I would like to be able to use detector.getFocuxX() and detector.getFocusY() to zoom into the
focus point, just like the pinch to zoom works in the android browser.
Basically I need to adjust the position of the viewport (xLoc and yLoc) so that it gives the appearance of zooming into the focus point of the scale gesture.
I cannot figure this part out. If anyone has an idea of how I can do this without using the canvas matrix transformations (which I have seen used in many of the topics I found before posting this) I would really appreciate it! Even if you could point me to someone else who has done something similar.
A single onScale function call does not provide enough information to implement scaling around apparent focus point. You will need to preserve values of getFocusX() and getFocusY() from a previous onScale call and use them to influence your scaling in the next call.
Good day,
I have weird problem with drawing image. Code and pictures say more then words.
mCamera.save();
mCamera.rotateX(rotateX);
mCamera.getMatrix(mMatrix);
mCamera.restore();
canvas.save();
canvas.setMatrix(mMatrix);
// here I draw images
...
canvas.restore();
and results are at images below. First picture have angle 0, second around 45 degree and last something more. It depend on size of area (number of bitmaps) I want to draw. I notice that when I draw bitmaps that whole fits canvas bounds, it works fine. But problem is mainly when I draw images with something like (so with part outside of canvas)
canvas.drawBitmap(image, -100, -100, null)
and images (hmm, because I'm new here, I cannot post images directly ...)
ttp://locus.asamm.cz/data/_temp/1311873666794.png
http://locus.asamm.cz/data/_temp/1311873695945.png
http://locus.asamm.cz/data/_temp/1311873782054.png
So question is, if there is any working solution or at least any tip. Also if anyone experienced can tell if drawing with OpenGL can help with this and if so, please point me to any information source that can help me with drawing bitmaps that change a lot (it's map application so user move with map), because I still cannot find any simple and clear source of info.
Thanks to all very much
It's a limitation of the 2D drawing API. To do true 3D you should use OpenGL.
I expect you are trying to produce a google maps tilt/rotate effect which I have successfull done for google maps since they don't think we need an update ;-) and I needed it for my application, even if you are using your own tiles it's basically the same thing.
One thing that helps is realize that the rotations for the Camera Matrix are performed in the upper left corner.
So you should transform to the appropriate location (dependent on axis of rotation and your expected results) and then back, something like ... the effects you are getting were very similar to mine before I read the docs and figured that out.
camera.save();
camera.rotateX(tilt); // tilt forward or backward
camera.rotateY(0);
camera.rotateZ(angle); // Rotate around Z access (similar to canvas.rotate)
camera.getMatrix(cameraMatrix);
// This moves the center of the view into the upper left corner (0,0)
// which is necessary because Matrix always uses 0,0, as it's transform point
// use the center of the canvas, or the bottom center dependent on your results
cameraMatrix.preTranslate(-centerX, -centerY);
// NOTE: Camera Rotations logically happen here when the canvas has the matrix applied
// This happens after the camera rotations are applied, moving the view back to
// where it belongs, allowing us to rotate around the center or any point we choose
// Move it back after the rotations are applied
cameraMatrix.postTranslate(centerX, centerY);
camera.restore();
canvas.concat(cameraMatrix);
The other thing to do is to oversize the container so you don't have empty space (or at least limit it) after your tilt/rotate operations, you can do that by calculating the needed oversize, the diagonal of your display works for basic rotation, + a percentage based on tilt if you are using that.
Playing around with the pre/postTranslate can can give you some interesting results.
As to moving the bitmap I am not really sure why you are doing this, probably panning, but as long as the canvas is getting filled it shouldn't matter
Update
Here is how I oversize the canvas, I tried to use the LayoutParams to do this but didn't have much luck with it so on Measure was the way to go for me.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
super.onMeasure(View.MeasureSpec.makeMeasureSpec(scaledDimensionsW,
View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(scaledDimensionsH,
View.MeasureSpec.EXACTLY));
else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}