I am developing an Android Game. In this game some trains are moving on tracks. am able to move trains horizontally and vertically using two train images for horizontal train and vertical train separately.
Now i want to move train that is coming on horizontal track to vertical, i want to visualize it as it is now taking turn and moving on vertical track.
how can i do this, do i need to use so many images for each turning pixel, or is there any other way to turn train.
Please suggest
Thanks.
Edit:
am using draw to draw() image and update() to change its position.
public void draw(Canvas canvas) {
canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() /2), null);
}
public void update() {
if (!touched && !paused && !isDrag) {
x += (speed.getXv() * speed.getxDirection());
y += (speed.getYv() * speed.getyDirection());
}
if (!touched && !paused && isDrag) {
x += (speed.getXv() * speed.getxDirection())* 2;
y += (speed.getYv() * speed.getyDirection())* 2;
}
}
Your question probably belong to
http://gamedev.stackexchange.com
But, assuming you are using some sort of Canvas, it has a method that applies rotation around a specific point.
http://developer.android.com/reference/android/graphics/Canvas.html#rotate%28float%29
Related
I currently have the following code:
private void drawGreen(Canvas canvas) {
greenPaint.setColor(0xFF00AA00);
if (start) {
greenPath = new Path();
greenPath.reset();
greenPath.moveTo(pathArrayX.get(0), pathArrayY.get(0));
start = false;
}
if (isInsideCircle(pathArrayX.get(pathIndex), pathArrayY.get(pathIndex), curX, curY, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25, getResources().getDisplayMetrics()))) {
greenPath.lineTo(pathArrayX.get(pathIndex), pathArrayY.get(pathIndex));
canvas.drawPath(greenPath, greenPaint);
pathIndex++;
}
}
private boolean isInsideCircle(float x, float y, float centerX, float centerY, float radius) {
return Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2) < Math.pow(radius, 2);
}
In my app, I at first draw a red path, with its coordinates stored in the ArrayLists pathArrayX and pathArrayY. I am tracking the X and Y coordinates of a circular ImageView being moved underneath a mouse, and would like to overlay the red path with a green path when the user hovers over the path from beginning to end. As the user hovers over the red path, the portion of the red path that they already completed would be overlaid by a green path along the same segment. The X and Y coordinates of the ImageView (curX and curY) are being calculated from a running thread.
However, my app doesn't appear to be drawing the green path at all. Is there anything I am doing wrong here?
Is this function even being called?
Assuming it's being called inside onDraw(Canvas), it looks like it might be missing the outer code for a loop. Seeing that you're doing pathIndex++ at the end, were you using a while loop? If you're just going to loop through point, use a for-loop as while-loop is more prone to dropping into an endless loop if you forgot to increment counter or doing wrongly, or do it in multiple places.
Side notes: if the boolean start flag is only being used to lazily initialise greenPath, you should scrap that and just use if (greenPath == null){ as a general practise. Use states that you can directly infer from objects and not use flags if you can help it, this makes code cleaner.
Background
I'm developing an app for Android that plots data as a line graph using AndroidPlot. Because of the nature of the data, it's important that it be pannable and zoomable. I'm using AndroidPlot's sample code on bitbucket for panning and zooming, modified to allow panning and zooming in both X and Y directions.
Everything works as desired except that there are no X and Y axis lines. It is very disorienting to look at the data without them. The grid helps, but there's no guarantee that grid lines will actually fall on the axis.
To remedy this I have tried adding two series, one that falls on just the X axis and the other on the Y. The problem with this is that if one zooms out too far the axis simply end, and it becomes apparent that I have applied a 'hack'.
Question
Is it possible to add X and Y axis lines to AndroidPlot? Or will my sad hack have to do?
EDIT
Added tags
I figured it out. It wasn't trivial, took a joint effort with a collaborator, and sucked up many hours of our time.
Starting with the sample mentioned in my question, I had to extend XYPlot (which I called GraphView) and override the onPreInit method. Note that I have two PointF's, minXY and maxXY, that are defined in my overridden XYPlot and manipulated when I zoom or scroll.
#Override
protected void onPreInit() {
super.onPreInit();
final Paint axisPaint = new Paint();
axisPaint.setColor(getResources().getColor(R.color.MY_AXIS_COLOR));
axisPaint.setStrokeWidth(3); //or whatever stroke width you want
XYGraphWidget oldWidget = getGraphWidget();
XYGraphWidget widget = new XYGraphWidget(getLayoutManager(),
this,
new SizeMetrics(
oldWidget.getHeightMetric(),
oldWidget.getWidthMetric())) {
//We now override XYGraphWidget methods
RectF mGridRect;
#Override
protected void doOnDraw(Canvas canvas, RectF widgetRect)
throws PlotRenderException {
//In order to draw the x axis, we must obtain gridRect. I believe this is the only
//way to do so as the more convenient routes have private rather than protected access.
mGridRect = new RectF(widgetRect.left + ((isRangeAxisLeft())?getRangeLabelWidth():1),
widgetRect.top + ((isDomainAxisBottom())?1:getDomainLabelWidth()),
widgetRect.right - ((isRangeAxisLeft())?1:getRangeLabelWidth()),
widgetRect.bottom - ((isDomainAxisBottom())?getDomainLabelWidth():1));
super.doOnDraw(canvas, widgetRect);
}
#Override
protected void drawGrid(Canvas canvas) {
super.drawGrid(canvas);
if(mGridRect == null) return;
//minXY and maxXY are PointF's defined elsewhere. See my comment in the answer.
if(minXY.y <= 0 && maxXY.y >= 0) { //Draw the x axis
RectF paddedGridRect = getGridRect();
//Note: GraphView.this is the extended XYPlot instance.
XYStep rangeStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.RANGE,
paddedGridRect, getCalculatedMinY().doubleValue(),
getCalculatedMaxY().doubleValue());
double rangeOriginF = paddedGridRect.bottom;
float yPix = (float) (rangeOriginF + getRangeOrigin().doubleValue() * rangeStep.getStepPix() /
rangeStep.getStepVal());
//Keep things consistent with drawing y axis even though drawRangeTick is public
//drawRangeTick(canvas, yPix, 0, getRangeLabelPaint(), axisPaint, true);
canvas.drawLine(mGridRect.left, yPix, mGridRect.right, yPix, axisPaint);
}
if(minXY.x <= 0 && maxXY.x >= 0) { //Draw the y axis
RectF paddedGridRect = getGridRect();
XYStep domianStep = XYStepCalculator.getStep(GraphView.this, XYAxisType.DOMAIN,
paddedGridRect, getCalculatedMinX().doubleValue(),
getCalculatedMaxX().doubleValue());
double domainOriginF = paddedGridRect.left;
float xPix = (float) (domainOriginF - getDomainOrigin().doubleValue() * domianStep.getStepPix() /
domianStep.getStepVal());
//Unfortunately, drawDomainTick has private access in XYGraphWidget
canvas.drawLine(xPix, mGridRect.top, xPix, mGridRect.bottom, axisPaint);
}
}
};
widget.setBackgroundPaint(oldWidget.getBackgroundPaint());
widget.setMarginTop(oldWidget.getMarginTop());
widget.setMarginRight(oldWidget.getMarginRight());
widget.setPositionMetrics(oldWidget.getPositionMetrics());
getLayoutManager().remove(oldWidget);
getLayoutManager().addToTop(widget);
setGraphWidget(widget);
//More customizations can go here
}
And that was that. I sure wish this was built into AndroidPlot; it'll be nasty trying to fix this when it breaks in an AndroidPlot update...
I'm trying to rotate my Bitmap using a readymade solution I found somewhere. The code is below:
public void onDraw(Canvas canvas) {
float x = ship.Position.left;
float y = ship.Position.top;
canvas.drawBitmap(ship.ship, x,y,null);
invalidate();
}
However, when I do it, the X and Y axii change their direction - if I increase the Y the image goes towards the top of the screen, not towards the bottom. Same happens to X if I rotate by 90 degrees.
I need to rotate it but without changing the Y and X axii directions.
Even rotated, I still want the Bitmap to go towards the bottom if I increase Y and to the right if I increase the X.
public void update()
{
if(!moving)
{
fall();
}
else //moving
{
move();
faceDirection();
}
Position.top += Speed;
}
private void move() {
if(Speed < MAXSPEED)
Speed -= 0.5f;
}
private void fall() {
if(Speed > MAXSPEED*-1)
Speed += 0.2f;
}
private void faceDirection() {
double OldDiretion = Direction;
Direction = DirectionHelper.FaceObject(Position, ClickedDiretion);
if (Direction != OldDiretion)
{
Matrix matrix = new Matrix();
matrix.postRotate((float)Direction);
ship = Bitmap.createBitmap(ship, 0, 0, ship.getWidth(),ship.getHeight(), matrix, false);
}
I tried the code above, but it's still changing the Y direction, It's going to bottom of the BitMap, not bottom of the screen.
Here is the project: https://docs.google.com/file/d/0B8V9oTk0eiOKOUZJMWtsSmUtV3M/edit?usp=sharing
You should first rotate, than translate:
matrix.postTranslate(x, y);
matrix.postRotate(degree);
alternative would be to try to use preRotate() instead of postRotate().
I also strongly recommend to translate/rotate the original while drawing. So your createBitmap() call shouldn't modify the orientation. At least not when you change it dynamically on user interaction. Otherwise you would create a lot of bitmaps to represent rotations over and over again which would impact the performance.
The problem is that you don't actually rotate the bitmap - you just draw it rotated. So, the next time you redraw it, you first push it towards the bottom or right by incrementing x/y and then rotate it.
You have to actually rotate the bitmap itself. You could use the following code:
ship.ship = Bitmap.createBitmap(ship.ship, 0, 0, ship.ship.getWidth(), ship.ship.getHeight(), matrix, false);
Here you create a new rotated bitmap and set your reference to point to it.
Note! You must do this only once! So you can't do it in the onDraw method, since then it will get rotated every time it's redrawn. You have to do it somewhere else and then draw it as usual in onDraw (without the matrix rotations).
I am trying to gain some more familiarity with the Android SurfaceView class, and in doing so am attempting to create a simple application that allows a user to move a Bitmap around the screen. The troublesome part of this implementation is that I am also including the functionality that the user may drag the image again after it has been placed. In order to do this, I am mapping the bitmap to a simple set of coordinates that define the Bitmap's current location. The region I am mapping the image to, however, does not match up with the image.
The Problem
After placing an image on the SurfaceView using canvas.drawBitmap(), and recording the coordinates of the placed image, the mapping system that I have set up misinterprets the Bitmap's coordinates somehow and does not display correctly. As you can see in this image, I have simply used canvas.drawLine() to draw lines representing the space of my touch region, and the image is always off and to the right:
The Code
Here, I shall provide the relevant code excerpts to help answer my question.
CustomSurface.java
This method encapsulates the drawing of the objects onto the canvas. The comments clarify each element:
public void onDraw(Canvas c){
//Simple black paint
Paint paint = new Paint();
//Draw a white background
c.drawColor(Color.WHITE);
//Draw the bitmap at the coordinates
c.drawBitmap(g.getResource(), g.getCenterX(), g.getCenterY(), null);
//Draws the actual surface that is receiving touch input
c.drawLine(g.left, g.top, g.right, g.top, paint);
c.drawLine(g.right, g.top, g.right, g.bottom, paint);
c.drawLine(g.right, g.bottom, g.left, g.bottom, paint);
c.drawLine(g.left, g.bottom, g.left, g.top, paint);
}
This method encapsulates how I capture touch events:
public boolean onTouchEvent(MotionEvent e){
switch(e.getAction()){
case MotionEvent.ACTION_DOWN:{
if(g.contains((int) e.getX(), (int) e.getY()))
item_selected = true;
break;
}
case MotionEvent.ACTION_MOVE:{
if(item_selected)
g.move((int) e.getX(), (int) e.getY());
break;
}
case MotionEvent.ACTION_UP:{
item_selected = false;
break;
}
default:{
//Do nothing
break;
}
}
return true;
}
Graphic.java
This method is used to construct the Graphic:
//Initializes the graphic assuming the coordinate is in the upper left corner
public Graphic(Bitmap image, int start_x, int start_y){
resource = image;
left = start_x;
top = start_y;
right = start_x + image.getWidth();
bottom = start_y + image.getHeight();
}
This method detects if a user is clicking inside the image:
public boolean contains(int x, int y){
if(x >= left && x <= right){
if(y >= top && y <= bottom){
return true;
}
}
return false;
}
This method is used to move the graphic:
public void move(int x, int y){
left = x;
top = y;
right = x + resource.getWidth();
bottom = y + resource.getHeight();
}
I also have 2 methods that determine the center of the region (used for redrawing):
public int getCenterX(){
return (right - left) / 2 + left;
}
public int getCenterY(){
return (bottom - top) / 2 + top;
}
Any help would be greatly appreciated, I feel as though many other StackOverflow users could really benefit from a solution to this issue.
There's a very nice and thorough explanation of touch/multitouch/gestures on Android Developers blog, that includes free and open source code example at google code.
Please, take a look. If you don't need gestures -- just skip that part, read about touch events only.
This issue ended up being much simpler than I had thought, and after some tweaking I realized that this was an issue of image width compensation.
This line in the above code is where the error stems from:
c.drawBitmap(g.getResource(), g.getCenterX(), g.getCenterY(), null);
As you can tell, I manipulated the coordinates from within the Graphic class to produce the center of the bitmap, and then called canvas.drawBitmap() assuming that it would draw from the center outward.
Obviously, this would not work because the canvas always drops from the top left of an image downwards and to the right, so the solution was simple.
The Solution
Create the touch region with regards to the touch location, but draw it relative to a distance equal to the image width subtracted from the center location in the x and y directions. I basically changed the architecture of the Graphic class to implement a getDrawX() and getDrawY() method that would return the modified x and y coordinates of where it should be drawn in order to have the center_x and center_y values (determined in the constructor) actually appear to be at the center of the region.
It all comes down to the fact that in an attempt to compensate for the way the canvas draws bitmaps, I unfortunately incorporated some bad behaviors and in the end had to handle the offset in a completely different way.
I have many images that I need to place on a canvas over a long period of time so that they look random. However, I don't want any of the images to overlap with each other. My solution so far is to randomly place the image somewhere on the canvas. If it overlaps I'll generate a new random location to try.
Now the tricky part is to see if where I am about to place the image is going to overlap with another image.
I was going to make a large array of 1's and 0's and manually mark off where I put the images. However, I was wondering if anyone knew of a way to "auto detect" using a method if where I am about to place an image will overlap with an existing image? Or if there is a way to do collision detection using some Android function?
Checking to see if two rectangles overlap is really simple, just use Rect.intersect()
Check out the Rect docs for more information:
http://developer.android.com/reference/android/graphics/Rect.html
Although I would recommend you try something different than what you have described above. In the beginning the probability of a collision will be very low. However as the screen fills up the probability of a collision will rise. This result in a lot of collisions and wasted computational power.
You should use something more efficient, off the top of my head you could try something like this:
Split the screen into a grid of size MxN
Keep a list of all unpopulated grid locations
Pick a random grid location for a new image i
Pick a random width and height for image i
If i intersects a grid location that is already populated or it if goes off the screen shrink it
Draw i
If all grid locations are taken quit, else go to 3
A simple 2D isinbox function could be:
bool IsInBox(int x1, int y1, int width1, int height1, int x2, int y2, int width2, int height2) {
int right1 = x1 + width1;
int right2 = x2 + width2;
int bottom1 = y1 + height1;
int bottom2 = y2 + height2;
// Check if top-left point is in box
if (x2 >= x1 && x2 <= right1 && y2 >= y2 && y2 <= bottom1) return true;
// Check if bottom-right point is in box
if (right2 >= x1 && right2 <= right1 && bottom2 >= y2 && bottom2 <= bottom1) return true;
return false;
}
Not sure if works though xd
Or you could use Rect.Intersect()