I hope this hasn't been asked too much before. When I search I only get questions pertaining to rescaling to window size.
Now my question. I got one space ship firing a beam against another ship. I want the beam to show for some time and I want it to "bridge" the two ships. In other words, I want the beam to extend its width between the two ships.
I try to do this with a dot movie clip that is 1 pixel wide and high (and aligned left edge). I try to resize it with the following code: (target is the ship to be fire at and owner is the ship firing)
dist.vx = target.x - owner.x;
dist.vy = target.y - owner.y;
dist.dist = Math.sqrt(dist.vx*dist.vx + dist.vy*dist.vy);
width = dist.dist;
x = owner.x;
y = owner.y;
rotation = Math.atan2(target.y-y, target.x-x)*180/Math.PI;
This doesn't work as intended because 1) dot also gets alot bigger in the other dimension - how can I "turn off" this behavior? and 2) sometimes it seems to get way to wide - but only in certain angles...
Any suggestions on either solving the heigh/width scaling or on another way to achieve the same effect?
(I'm new to coding and flash.) Thanks!
By resizing a dot, you will have a rectangle...
You can dynamically create a sprite covering both ships and moveTo the hit point of one ship then lineTo the other ship... You do not need distance calculation at all. What you have to do is being careful on the placement of the sprite. So that you can calculate relative hitting points by simple math.
Suppose you have mc space contining mc ship1 and mc ship2, and hit point coords on ships are named hx, hy and you will use sprite s, calculation will be as follows.
// calculate hit points relative to mc space
var s1HX:int = ship1.x + ship1.hx,
s1HY:int = ship1.y + ship1.hy,
s2HX:int = ship2.x + ship2.hx,
s2HY:int = ship2.y + ship2.hy,
// sprite relative moveTo lineTo coords will be these.
mX: int, mY: int,
lX: int, lY: int;
// top left of sprite will be minimum of the hit coords.
s.x = (s1HX <= s2HX)? s1HX : s2HX;
s.y = (s1HY <= s2HY)? s1HY : s2HY;
// now we can get sprite relative moveTo lineTo coordinates:
mX = s1HX - s.x;
mY = s1HY - s.y;
lX = s2HX - s.x;
lY = s2HY - s.y;
The rest is implementation with using these with fancy line styles etc...
To create a new sprite:
var s:Sprite = new Sprite();
Adding / removing it to/from mc space:
space.addChild(s);
space.removeChild(s);
For graphics use the graphics object of sprite.
s.graphics
For setting line styles you can use:
s.graphics.lineStyle(...) ,
s.graphics.lineBitmapStyle(...),
s.graphics.lineGradientStyle(...)
Functions, please read the manual for usage.
After setting the line style to draw the line use:
s.graphics.moveTo(mX,mY);
s.graphics.lineTo(lX,lY);
For pulsating effects you have to do a little more complicated things such as using tween class which you can read about here: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/fl/transitions/Tween.html
Note that:
Sprites are no complicated magic, they are like mc's but they do not have timelines etc.
Sprites try to scale when width or height change programmatically. So do not touch them, moveTo lineTo automatically sets the size of a sprite...
Related
The past few days I've been trying to figure out a display bug I don't understand. I've been working on a simple 2d platformer with box2d and orthogonal Tiled maps. So far so good, the physics work and using the b2d debug renderer I can assert proper player fixture and camera movement through the level.
Now next step I've tried to load textures to display sprites instead of debug shapes. This is where I stumble. I can load animations for my player body/fixture, but when I use the setCenter() method to center the texture on the fixture it is always out of center.
I've tried approaches via halving texture witdths and heights hoping to center the texture on the player fixture but I get the exact same off position rendering. I've played aorund with world/camera/screen unit coordinates but the misalignement persists.
I'm creating the player in my Player class with the following code.
First I define the player in box2d:
//define player's physical behaviour
public void definePlayer() {
//definitions to later use in a body
BodyDef bdef = new BodyDef();
bdef.position.set(120 / Constants.PPM, 60 / Constants.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
b2body = world.createBody(bdef);
//Define needed components of the player's main fixture
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(8 / Constants.PPM, 16 / Constants.PPM); //size of the player hitbox
//set the player's category bit
fdef.filter.categoryBits = Constants.PLAYER_BIT;
//set which category bits the player should collide with. If not mentioned here, no collision occurrs
fdef.filter.maskBits = Constants.GROUND_BIT |
Constants.GEM_BIT |
Constants.BRICK_BIT |
Constants.OBJECT_BIT |
Constants.ENEMY_BIT |
Constants.TREASURE_CHEST_BIT |
Constants.ENEMY_HEAD_BIT |
Constants.ITEM_BIT;
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
}
Then I call the texture Region to be drawn in the Player class constructor:
//define in box2d
definePlayer();
//set initial values for the player's location, width and height, initial animation.
setBounds(0, 0, 64 / Constants.PPM, 64 / Constants.PPM);
setRegion(playerStand.getKeyFrame(stateTimer, true));
And finally, I update() my player:
public void update(float delta) {
//center position of the sprite on its body
// setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2);
setCenter(b2body.getPosition().x, b2body.getPosition().y);
setRegion(getFrame(delta));
//set all the boolean flags during update cycles approprietly. DO NOT manipulate b2bodies
//while the simulation happens! therefore, only set flags there, and call the appropriate
//methods outside the simulation step during update
checkForPitfall();
checkIfAttacking();
}
And my result is
this, facing right
and this, facing left
Update:
I've been trying to just run
setCenter(b2body.getPosition().x, b2body.getPosition().y);
as suggested, and I got the following result:
facing right and facing left.
The sprite texture flip code is as follows:
if((b2body.getLinearVelocity().x < 0 || !runningRight) && !region.isFlipX()) {
region.flip(true, false);
runningRight = false;
} else if ((b2body.getLinearVelocity().x > 0 || runningRight) && region.isFlipX()) {
region.flip(true, false);
runningRight = true;
}
I'm testing if either the boolean flag for facing right is set or the x-axis velocity of my player b2body has a positive/negative value and if my texture region is already flipped or not and then use libGDX's flip() accordingly. I should not be messing with fixture coords anywhere here, hence my confusion.
The coordinates of box2d fixtures are offsets from the position, the position isn't necessarily the center (although it could be depending on your shape definition offsets). So in your case i think the position is actually the lower left point of the box2d polygon shape.
In which case you don't need to adjust for width and height because sprites are also drawn from bottom left position. So all you need is ;
setPosition(b2body.getPosition().x , b2body.getPosition().y );
I'm guessing you flip the box2d body when the player looks left the position of the shape is now bottom right so the sprite offset of width/2 and height/2 is from the bottom right instead. So specifically when you are looking left you need an offset of
setPosition(b2body.getPosition().x - getWidth() , b2body.getPosition().y );
I think looking right will be fixed from this, but i don't know for sure how you handle looking left in terms of what you do to the body, but something is done because the offset changes entirely as shown in your capture. If you aren't doing some flipping you could add how you handle looking right to the question.
EDIT
It seems the answer was that the sprite wasn't centered in the sprite sheet and this additional space around the sprite caused the visual impression of being in the wrong place (see comments).
When I run this code it does not display the sprite on the screen . just a blank screen I have tried everything I can think of to get this to work .Some help would be much appreciated.
I have tried everything I can think of to get this to work. what I'm trying to do is create my sprites with a rect attribute.
import pygame
pygame.display.init()
pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
x = 300
y = 500
x1 = 100
y1 = 200
image1 = pygame.sprite.Sprite()
image1.image = pygame.image.load("picy.png").convert_alpha()
image2 = pygame.sprite.Sprite()
image2.image = pygame.image.load("picy1.png").convert_alpha()
image1_rect = image1.image.get_rect(topleft=(x,y))
image2_rect = image2.image.get_rect(topleft=(x1,y1))
screen.blit(image2_rect,(x1,y1))
screen.blit(image1_rect,(x,y))
pygame.display.update()
I expect it to put my two sprites on the screen and when they touch for them to register a hit.
From the docs:
blit()
draw one image onto another
blit(source, dest, area=None, special_flags = 0) -> Rect
Draws a source Surface onto this Surface. The draw can be positioned with the dest argument. Dest can either be pair of coordinates representing the upper left corner of the source. A Rect can also be passed as the destination and the topleft corner of the rectangle will be used as the position for the blit. The size of the destination rectangle does not effect the blit.
The blit method takes as first argument a Surface, not a Rect. The Surface is your image.
Try with:
screen.blit(image2.image, dest=image2_rect)
screen.blit(image1.image, dest=image1_rect)
By the way, you may also wish to make the rectangles attributes of the Sprite instances, instead of separate instances:
image1.rect = image1.image.get_rect(topleft=(x,y))
image2.rect = image2.image.get_rect(topleft=(x1,y1))
I need to detect when MC2 is over MC1 that it is inside MC1's borders.
to do this I would usually use 4 separate if x y constraints,
and unfortunately .hitTestObject in my creations also seem to need 4 separate if x y + - constraints.
Does anyone know a more simplistic way to achieve this.
or is x y + - constraints still the only way to do this?
Thank you in advance.
The final solution for your problem to detect hit of two shapes, is to use bitmapData.hitTest(). you can detect hit between any shapes and not only Rectangles. for that, you have to draw both of your shapes on bitmapData like line belo:
var shape1Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
var shape2Bitmap:BitmapData = new BitmapData(shape1MC.with,shape1MC.height,true,0x000000);
shape1Bitmap.draw(shape1MC);
shape1Bitmap.hitTest(new Point(),shape2Bitmap):Boolean;******
to continue usint BitmapData.hitTest(), folow the orders here : https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#hitTest()
http://dougmccune.com/blog/2007/02/03/using-hittestpoint-or-hittest-on-transparent-png-images/
It is a little complicated to add the bitmapData.hitTest() samples here. if any further questions left, please let me know to explain.
Good luck
I don't know of a built in way to do this, but it's easy enough using hitTestPoint with each corner of the square:
function isSquareInsideObject(square:DisplayObject, obj:DisplayObject):Boolean {
if(!obj.hitTestPoint(square.x, square.y, true)) return false;
if(!obj.hitTestPoint(square.x + square.width, square.y, true)) return false;
if(!obj.hitTestPoint(square.x + square.width, square.y + square.height, true)) return false;
if(!obj.hitTestPoint(square.x, square.y + square.height, true)) return false;
return true;
}
For more complex shapes than a square, you'd have to add more points to be accurate and it becomes a less elegant and less performant solution then.
You need that shape argument (third parameter for hitTestPoint) set to true if you want to test against the actual circle shape instead of the rectangular bounding box of the circle. If your circle is a bitmap (and not a shape), then I'd suggest putting a circular mask on the object to achieve the same result.
If your square isn't anchored at 0,0, or you don't mind the extra (small) performance hit, you could also use var bounds:Rectangle = square.getBounds(this) and then use the convenience properties of the rectangle object (bounds.bottomLeft, bottomRight, topLeft, topRight)
Scrolling graph. Currently I show a real-time graph by drawing each piece of data as it is generated. To scroll, I simply move the container object left to compensate and erase all the data left of the bounds.
Simple, fast, surprisingly memory efficient...but is there a concern of the coordinates overflowing as I keep adding contents to the right and scrolling the container left? Is there a limit on the x coordinate of an (empty) object? I'm afraid if I keep scrolling the container left indefinitely and writing to ever increasing x coordinates the program would misbehave eventually. Would this occur? If so, at what max coordinates? Or does AS3 handle this automatically?
I did a test, where I keep drawing an item to a container making it bigger and bigger, and moving it to the left. It stopped getting bigger at 105,000,000 pixels. After that the width reported 0 and the x property reported -107,374,182.4 and wouldn't move beyond that.
If you have bitmap data though, the FP10 limit is 8,191 pixels in width or height, and the total number of pixels cannot exceed 16,777,215 pixels.
Here was my code used to test:
var b:Sprite = new Sprite();
addChild(b);
b.x = stage.stageWidth * .5;
var t:Timer = new Timer(100);
t.addEventListener(TimerEvent.TIMER,tick);
t.start();
var moveAmt:Number = 50;
function tick(e:Event):void {
b.x -= moveAmount;
b.graphics.beginFill(Math.random() * 0xFFFFFF);
b.graphics.drawRect(b.width,0,moveAmount,stage.stageHeight);
b.graphics.endFill();
trace(b.width + " : " + b.x);
}
I'm just trying to avoid rolling my own dragging functionality. Does anyone know of any libraries out there that have a startDrag() equivalent where you can use, say, a circular radius for the drag bounds, rather than a rectangular box?
(For circular drag area) - What you need to do is:
a) Mouse_down: Store start position. Start listening to Enter_frame.
b) Enter_Frame: Check distance from mouse position of mouse to start pos (use pythagoras)
c) only move your object if the distance is less than x
d) Mouse_up: Stop listening to enterframe
You can use a simple circular collision detection routine, it works out the hit area using the radius of the objects and distance between them. Maybe you will have to manually do this calculation in your onDrag method and stop the drag on collision with the circular bounds calculated below.
var deltax : Number = targetCentreCoord.x - hitTestCentreCoord.x;
var deltay : Number = targetCentreCoord.y - hitTestCentreCoord.y;
//works out if our circles are colliding, distance between the circles inc radius
if (((deltax * deltax) + (deltay * deltay)) <= ((((targetRadius) + (hitTargetRadius)) * ((targetRadius) + (hitTargetRadius)))))
{
Log.info("collision occured with " + candidate.name + " target coords " + targetCentreCoord + " candidate coords " + hitTestCentreCoord);
return true;
}
return false;
Nope, you need to do pixel-perfect collision (or in this case, mouse clicking) in order to do that. By nature all display objects always have rectangular bounds to them. So basically you'd have to do something like this:
mySprite.addEventListener(MouseEvent.MOUSE_DOWN, mousedDown);
function mousedDown(e:MouseEvent):void
{
//Draw my sprite to a bitmap, then check the bitmap colour at mouseX/mouseY
uint colour = myBitmap.getPixel32(mouseX, mouseY);
if(colour != TRANSPARENT){
//We've actually clicked on the object, drag it
Sprite(e.currentTarget).startDrag();
}
}
Note this is just pseudo code, you'll have to figure out what uint value transparent comes up as, and also you'll have to account for where the origin point of the sprite is when drawing to bitmap. Say you have a sprite and the contents are inside, you're going to need to create a Matrix object that has a X and Y offset that are negative .5 times the width of your sprite in order to draw it properly.
This can be done without ENTER_FRAME event.
Have a MOUSE_DOWN listener, check boundaries there, if within boundaries, then
add a MOUSE_MOVE listener.
Also, start out with a MOUSE_UP listener to remove the MOUSE_MOVE listener.