I'm trying to generate random ImageViews on a Layout without the ImageViews intersecting each other. I'm generating a random point within the screen dimensions like this:
private Point generateRandomLocation(Point dimensions) {
Random random = new Random();
// generate random x
int x = random.nextInt((dimensions.x - 0) + 1);
// generate random y
int y = random.nextInt((dimensions.y - 0) + 1);
Point location = new Point(x, y);
if(!collision(location)) {
return new Point(x, y);
} else {
return generateRandomLocation(dimensions);
}
}
The collision method contains the following method to determine wether the ImageViews collide or not. BubbleImage is a simple extension of ImageView.
private boolean collision(Point location) {
// takes 100 as inital width & height
int x_1 = location.x;
int y_1 = location.y;
int x_2;
int y_2;
boolean collided = false;
// get all bubbleimages
for (int i = 0; i < mainLayout.getChildCount(); i++) {
View childView = mainLayout.getChildAt(i);
if (childView instanceof BubbleImage) {
x_2 = (int) childView.getX();
y_2 = (int) childView.getY();
// create rectangles
Rect rect1 = new Rect(x_1, y_1, x_1 + 100, y_1 - 100);
Rect rect2 = new Rect(x_2, y_2, x_2 + 100, y_2 - 100);
collided = Rect.intersects(rect1, rect2);
}
}
return collided;
}
Anyone spotting the faulty logic here?
Edit: Rect.intersects() seems to be returning false, even when the imageviews are intersecting.
When creating new Rects rect1 & rect2, the constructor is Rect(left, top, right, bottom). E.g. Rect(10, 10, 20, 20), because android screen origin is in the top left corner.
You have created the Rects in a wrong way, as Rect(left, bottom, right, top). Try switching the 2nd and 4th parameters in the constructor calls, or increase the 4th parameter to be bigger than 2nd.
Like this:
Rect rect1 = new Rect(x_1, y_1, x_1 + 100, y_1 + 100);
Related
I have set up a game where Actor fireflies randomly fly across the screen, while glowing on and off — and a user can drag them into a mason jar. Pretty happy I've got this working, but I'd like to add a bit more detail. I'd like to add a two-step animation so that it looks like their wings are flapping.
I know how to do this with the Animation class, making use of TextureAtlas and TextureRegion. But that was prior to me heading in this Drag n Drop direction.
My issue, I think, is that I'm using skins, and they might not play nice with Animations.
///////////////////
Portions of my code.
///////////////////
Some of the items I declare up top:
private TextureAtlas textureAtlas;
private Texture texture;
private TextureRegion[] regions = new TextureRegion[3];
private Animation ffFlapping;
Setting up my TextureAtlas:
textureAtlas = new TextureAtlas(Gdx.files.internal("Player Animation/player_animation.png.atlas"));
texture = new Texture(Gdx.files.internal("Player Animation/player_animation.png.png"));
Setting up the skin:
final Skin skin = new Skin();
skin.addRegions(textureAtlas);
Doing the animation:
TextureRegion[] ffAnimation = new TextureRegion[2];
ffAnimation[0] = (textureAtlas.findRegion("firefly-0"));
ffAnimation[1] = (textureAtlas.findRegion("firefly-1"));
ffFlapping = new Animation(0.01f, ffAnimation);
For loop to create all my fireflies:
// ********************************************
// iterate through the number of fireflies
// we want to draw out using fireflyCount
// ********************************************
for (int fireflyIndex = 0; fireflyIndex < fireflyCount; fireflyIndex++) {
// YELLOW FIREFLY HERE
String fireflyName = "firefly" + fireflyIndex;
// if I replace ffFlapping below with object, I get no errors,
// but also no animation
skin.add(fireflyName, ffFlapping);
final Firefly ff = new Firefly(skin, fireflyName);
System.out.println("Fireflies objects:" + fireflyIndex);
// Not sure this affected the color, but it starts the alpha at 0
ff.setColor(150, 150, 150, 0);
ff.setOrigin(ff.getWidth()/2, ff.getHeight()/2);
stage.addActor(ff);
// This was set up in the attempt to continue movement if user misses target
final MoveToAction actionRight = new MoveToAction();
final MoveToAction actionLeft = new MoveToAction();
// setting up right and left targets, with a random Y position
float toRight = Gdx.graphics.getWidth() + 60;
float toLeft = (Gdx.graphics.getWidth() -Gdx.graphics.getWidth())-60f;
// sets up speed of glow, and the random time firefly is off, and also on
float glow = MathUtils.random(.5f, 1f);
float delayRandomOff = MathUtils.random(2.3f, 4.5f);
float delayRandomOn = MathUtils.random(.5f, .9f);
// sets up first variable to randomly choose between toRight and toLeft
// assigns direction to that value
float randomDirection = MathUtils.random.nextBoolean() ? toRight : toLeft;
float direction = randomDirection;
SequenceAction sequence = new SequenceAction();
AlphaAction aa = new AlphaAction();
Action alphaStartOn = Actions.delay(delayRandomOn, Actions.fadeOut(glow));
Action alphaStartOff = Actions.delay(delayRandomOff, Actions.fadeIn(glow));
// toRight is the x value ... it goes (x, y, duration)
Action startRight = Actions.moveTo(toRight, MathUtils.random(50, Gdx.graphics.getHeight() - 40), MathUtils.random(10f, 45f));
// toLeft is the x value ... it goes (x, y, duration)
// 170 makes sure they don't fly on top of mason jar
Action startLeft = Actions.moveTo(toLeft, MathUtils.random(170, Gdx.graphics.getHeight() - 40), MathUtils.random(10f, 45f));
Action faceOpposite = Actions.rotateBy(180f);
Action faceOpposite2 = Actions.rotateBy(180f);
// THIS ENDLESSLY LOOPS THEM ON THE SCREEN
Action loopRight = Actions.forever(Actions.sequence(faceOpposite, startRight, faceOpposite2, startLeft));
Action loopLeft = Actions.forever(Actions.sequence(startLeft, faceOpposite, startRight, faceOpposite2));
Action loopGlow1 = Actions.forever(Actions.sequence(alphaStartOn, alphaStartOff));
Action loopGlow2 = Actions.forever(Actions.sequence(alphaStartOff, alphaStartOn));
// THIS IS DEFINITELY TRIGGERING THE MOVEMENT
if(direction == toRight) {
ff.addAction(loopRight);
ff.addAction(loopGlow1);
} else {
ff.addAction(loopLeft);
ff.addAction(loopGlow2);
}
// MAKE EACH FIREFLY DRAGGABLE, and SET LARGER SIZE as you drag
dragAndDrop.addSource(new DragAndDrop.Source(ff) {
public DragAndDrop.Payload dragStart (InputEvent event, float x, float y, int pointer) {
DragAndDrop.Payload payload = new DragAndDrop.Payload();
payload.setObject("Firefly captured");
payload.setDragActor(ff);
ff.clearActions();
getActor().setSize(80, 80);
// Firefly freezes on drag, and enlarges ... disappears if dropped in jar
// Does not visually drag with cursor since it was part of animation.
return payload;
}
// IF YOU DON'T DROP FIREFLY ON TARGET, MAKE SURE IT STAYS ON STAGE
// AND GOES BACK TO THE NORMAL SIZE
#Override
public void dragStop(InputEvent event, float x, float y, int pointer, DragAndDrop.Payload payload, DragAndDrop.Target target) {
if(target == null)
stage.addActor(ff);
ff.addAction(actionLeft);
getActor().setSize(50, 50);
}
});
// MAKE EACH FIREFLY DRAGGABLE, and SET LARGER SIZE as you drag
dragAndDrop.addSource(new DragAndDrop.Source(ff) {
public DragAndDrop.Payload dragStart (InputEvent event, float x, float y, int pointer) {
DragAndDrop.Payload payload = new DragAndDrop.Payload();
payload.setObject("Firefly captured");
payload.setDragActor(ff);
ff.clearActions();
getActor().setSize(80, 80);
// Firefly freezes on drag, and enlarges ... disappears if dropped in jar
// Does not visually drag with cursor since it was part of animation.
return payload;
}
// IF YOU DON'T DROP FIREFLY ON TARGET, MAKE SURE IT STAYS ON STAGE
// AND GOES BACK TO THE NORMAL SIZE
#Override
public void dragStop(InputEvent event, float x, float y, int pointer, DragAndDrop.Payload payload, DragAndDrop.Target target) {
if(target == null)
stage.addActor(ff);
ff.addAction(actionLeft);
getActor().setSize(50, 50);
}
});
} // ***** END OF FOR LOOP ***********
And here's my Firefly code:
public class Firefly extends Image {
private float x = MathUtils.random(20, Gdx.graphics.getWidth() -40);
private float y = MathUtils.random(200, Gdx.graphics.getHeight() - 40);
private float width = 70;
private float height = 70;
public Firefly(Skin skin, String drawableName) {
super(skin, drawableName);
this.setBounds(x, y, width, height);
}
} // firefly
The error I'm getting is:
com.badlogic.gdx.utils.GdxRuntimeException: No Drawable, NinePatch, TextureRegion, Texture, or Sprite registered with name: firefly-2
///////////////////////////////
Any tips are very much appreciated.
In the meantime, I'm creating a new feature branch and keeping at it.
My guess is that I need to somehow make my two-step animation into some kind of Drawable.
Thanks!
— Bill
I am using CCDrawNode to create mask type effect (not exactly mask). Everything works well but there is one problem that CCDrawNode only draws square and i want to draw it with custom texture/sprite. Is there any solution to it.
Below is my code of using CCDrawNode
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !CCLayer::init() )
{
return false;
}
CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
CCLayer *layer = CCLayer::create();
CCSprite* pSprite = CCSprite::create("HelloWorld.png");
pSprite->setPosition(ccp(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
layer->addChild(pSprite, 0);
addChild(layer);
//this is the layer that we want to "cut"
CCLayer* layer1 = CCLayerColor::create(ccc4(122, 144, 0, 255), visibleSize.width, visibleSize.height);
this->setTouchEnabled(true);
//we need to create a ccnode, which will be a stencil for ccclipingnode, draw node is a good choice for that
stencil = CCDrawNode::create();
//CCClipingNode show the intersection of stencil and theirs children
CCClippingNode *cliper = CCClippingNode::create(stencil);
cliper->setInverted(true);
cliper->addChild(layer1);
addChild(cliper);
return true;
}
void HelloWorld::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
CCTouch* touch = (CCTouch*)touches->anyObject();
// get start & end location
CCPoint start = touch->getLocationInView();
CCPoint end = touch->getPreviousLocationInView();
// get corrected location
start = CCDirector::sharedDirector()->convertToGL(start);
end = CCDirector::sharedDirector()->convertToGL(end);
//stencil->drawDot(start, 25, ccc4f(0, 0, 0, 255));
stencil->drawSegment(start, end, 25, ccc4f(0, 0, 0, 255));
}
If you want to draw custom texture you should use CCRenderTexture. In order to draw something you should go smthin like this
myRenderTexture->begin();
mySpriteLoadedWithTexture->visit();
myRenderTexture->end();
Also if you want the drawn lines to be smooth you should draw it in loop so that they are placed in equal distance
float distance = ccpDistance(start, end);
for (int i = 0; i < distance; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
m_brush->setPosition(CCPoint(start.x + (difx * delta), start.y + (dify * delta)));
m_brush->visit();
}
Hope it helps
i am having a problem with canvas, rectangles and screen sizes:
I do not know whats wrong, because i am using the exact same code in both emulators, here is the code:
clouds is a plain object that contains the image and some methods to move them, this is how i create the clouds:
The clouds are all in one file, the coordinates are the rectangles where the single clouds are.
for (byte i = 0; i < 4; i++) {
Cloud c;
if (i == 1) c = new Cloud(5, 5, 245, 145);
else if (i == 2) c = new Cloud(340, 30, 480, 130);
else if (i == 3) c = new Cloud(565, 30, 725, 130);
else c = new Cloud(830, 20, 980, 130);
c.SetX(0);c.SetY(i * 150);
//c.ReubicarNube();
Clouds.Add(c);
}
The constructor of Cloud class call this to handle the images (Plain is the super class of cloud):
public Plain(int ResourceImg, int left, int top, int right, int bottom){
this.img = BitmapFactory.decodeResource(Init.GetContext().getResources(), ResourceImg);
this.Width = right - left;
this.Height = bottom - top;
this.frameImg = new Rect(left, top, right, bottom);
this.rect = new Rect(0, 0, this.Width, this.Height);
Log.v("Plains", this.frameImg.toString() + " " + this.rect.toString());
}
and this is how I draw:
public void draw(Canvas canvas) {
canvas.drawBitmap(img, frameImg, rect, Init.PaintFilter);
//canvas.drawbi
}
Thanks in advance for the help!
I did it!,
I fogot to put the image in the drawable-hdpi folder!, thats why i wasn't working!!
Thanks!
I have drawn multiple circles of varying sizes and positions to a canvas, but I need to detect collisions between them.
public void run() {
while(Run){
if(!sHold.getSurface().isValid())
continue;
c[0][cnum].r++;
canvas = sHold.lockCanvas();
canvas.drawRGB(02, 02, 150);
Paint white = new Paint();
white.setColor(Color.WHITE);
if(c[0][cnum].x != 0 && c[0][cnum].y != 0)
canvas.drawCircle(c[0][cnum].x, c[0][cnum].y, c[0][cnum].r, white);
if(cnum!=0)
for(int i=0; i<cnum; i++)
canvas.drawCircle(c[1][i].x, c[1][i].y, c[1][i].r, white);
sHold.unlockCanvasAndPost(canvas);
if(((c[0][cnum].x - c[0][cnum].r)<0)||((c[0][cnum].y-c[0][cnum].r)<0)||((c[0][cnum].y+c[0][cnum].r)>height)||((c[0][cnum].x+c[0][cnum].r>width))){
c[1][cnum].x = c[0][cnum].x;
c[1][cnum].y = c[0][cnum].y;
c[1][cnum].r = c[0][cnum].r;
broken = true;
break;
}
}
}
You shouldn't do that on render phases.
When processing the logics you should check if circles intersects as described:
v1 = center of circle1
v2 = center of circle2
intersects = v1 - v2 < circle1radius + circle2radius
This Link is pretty useful!
Circle-Circle Collisions
It's very detailed and didatic
At the bottom of that page there are another links, to even more detailed stuff!
I used the Distance Between Centers method ---
Circles
By measuring the distance between each center you can say if they are colliding.
The distance should never be more then the sum of the 2 radius.
Here's what I did:
private boolean checkDrawContains(ShapeDrawable newHole)
{
long newCenterX = newHole.getBounds().left + (newHole.getBounds().width()/2); //Get the center of my shapes
long newCenterY = newHole.getBounds().top + (newHole.getBounds().height()/2);
for(ShapeDrawable hole: mHoles) // I was storing the circles in an ArrayList
{
long centerX = hole.getBounds().left + (hole.getBounds().width()/2); //Get the center of my shapes
long centerY = hole.getBounds().top + (hole.getBounds().height()/2);
long x = centerX - newCenterX;
long y = centerY - newCenterY;
long aux = (long) ((Math.pow(Math.abs(x),2)) + (Math.pow(Math.abs(y),2))); //Pythagoras the hard way :P
long distance = (long) Math.sqrt(aux);
long sRads = (newHole.getBounds().width()/2) + (hole.getBounds().width()/2);
if(distance <= sRads ) {
return true; //Is Colliding!
}
}
return false; // Is not Colliding!
}
I am new to android and building an app which involves displaying a view for 2 seconds and then change. Here's my onDraw method:
#Override
public void onDraw(Canvas canvas)
{
float level = game.level;
width = getWidth();
tile_length = width/level;
Paint rect = new Paint();
rect.setColor(getResources().getColor(R.color.dark));
canvas.drawRect(0, 0, width, width, rect);
game.numbers.setTextSize( (0.70f * tile_length));
game.numbers.setTextAlign(Paint.Align.CENTER);
grid.setColor(getResources().getColor(R.color.lines));
rect.setColor(getResources().getColor(R.color.tile_on));
int ind = 1;
int tile_num = 1;
FontMetrics fm = game.numbers.getFontMetrics();
float x = tile_length/2;
float y = tile_length/2 - (fm.ascent + fm.descent) / 2;
Log.v(LOG_TAG, "changed = " + game.changed);
for (int i=0; i<width; i+=tile_length)
{
for(int j=0; j<width; j+=tile_length)
{
for(int k = 0; k<level; k++ )
if(tile_num == game.random[k])
{
// Log.v(LOG_TAG, "i = " + i + "j = " + j);
game.set_Coordinates(ind-1, i, j);
String tile = Integer.toString(ind++);
canvas.drawRect(i, j, i+tile_length, j+tile_length, rect);
canvas.drawText(tile, i+x, j+y, game.numbers); //needs to be updated after 2 seconds
break;
}
tile_num++;
}
}
}
I understand i have to use postdelayed method somewhere, but don't know how...Now i just want to ommit the canvas.drawText line after the delay.
do you mean something like this
new Handler().postDelayed(new Runnable(){
public void run(){
// do something here like draw text;
}
}, 2000);
A timer is needed, indeed. What I do, which is very simple, is first to create records of coordinates (and any other data needed) for every point of the drawing -- instead of drawing the points on the spot -- and then reproduce them using a timer (Android handler, preferably, like the one suggested above). This also offers you a lot of possibilities while actual drawing: pause, go faster/slower, go backwards, ...
I don't know if this method can be used for complicated drawings, but it is fine for drawing shapes, curves, surfaces, etc.