How to have an object hover back and forth constrained within a specific radius? - actionscript-3

I have a sprite in a movie symbol that I would like to hover back and forth within a 360 radius. I was hoping to make it smooth and random. Never really venturing from its original xy cordinates.
I've tried to create some stipulations with if statements and a starting momentum. Like this:
var num = 2;
stage.addEventListener(Event.ENTER_FRAME, hover);
function hover(evt:Event):void{
//start it moving
cloudWhite.y += num;
cloudWhite.x += num;
//declare these variables
var cX = cloudWhite.x;
var cY = cloudWhite.y;
// object travels 10 pixels
var cXP = cX + 10;
var cXN = cX - 10;
var cYP = cY + 10;
var cYN = cY - 10;
// if object goes 10 pixels reverse direction of momentum (maybe)
if (cX >= cXP) {
num = -2;
}
if (cX <= cXN){
num = 2;
}
if (cY >= cYP) {
num = 2;
}
if (cY <= cYN){
num = 2;
}
Clearly this is super wrong because when it runs the object just either goes to 0,0 or to some place that only the math gods know of.
I am clearly a noob at this kind of math so i apologize but I am very excited to learn the trig behind this.
Thank you for your help and thank you for reading.

You are setting all your variables inside the ENTER_FRAME loop, so none of your conditions ever evaluates to true. On every single frame you are doing this:
cloudWhite.x += 2;
cX = cloudWhite.x;
cXP = cX + 10; // Must == cloudWhite's previous x + 10 + 2;
cXN = cX - 10; // Must == cloudWite's previous x -10 + 2;
if(cX > cXP)... // Can never be true.
if(cX < cXN)... // Can never be true.
What you need to do is:
1) Store the original position of cloudWhite somewhere outside the loop, and store it before the loop begins.
2) Define your bounds relative to the original position of cloudWhite, again before your loop begins. Also define the amount you are going to change the position with each iteration.
3) Start your loop.
4) Increment the current position of cloudWhite on each iteration. Add a little random in here if you want the shape to move in a random manner.
5) Check if the new position of cW is outside your bounds and adjust the direction if it is.
The sample below is crude and jerky but I don't know exactly what effect you're looking for. If you want smoother, longer movements in each direction, consider using the Tween class or a Tween library such as the popular Greensock one, instead of incrementing / decrementing the position manually. There's a useful discussion of this here: http://www.actionscript.org/forums/archive/index.php3/t-163836.html
import flash.display.MovieClip;
import flash.events.Event;
// Set up your variables
var original_x:Number = 100; // Original x
var original_y:Number = 100; // Original y
var x_inc:Number = 5; // X Movement
var y_inc:Number = 5; // Y Movenent
var bounds:Number = 50; // Distance from origin allowed
// Doesn't take into account width of object so is distance to nearest point.
// Create an MC to show the bounds:
var display:MovieClip = addChild(new MovieClip()) as MovieClip;
display.graphics.lineStyle(1, 0x0000FF);
display.graphics.beginFill(0x0000FF, 0.5);
display.graphics.drawRect(0-bounds, 0-bounds, bounds * 2, bounds *2);
display.x = original_x;
display.y = original_y;
addChild(display);
// Create our moving mc:
var mc:MovieClip = addChild(new MovieClip()) as MovieClip;
mc.graphics.beginFill(0xFF0000, 1);
mc.graphics.drawCircle(-10, -10, 20);
// Position it:
mc.x = original_x;
mc.y = original_y;
addChild(mc);
// Loop:
function iterate($e:Event = null):void
{
// Move the mc by a random amount related to x/y inc
mc.x += (Math.random() * (2 * x_inc))/2;
mc.y += (Math.random() * (2 * y_inc))/2;
// If the distance from the origin is greater than bounds:
if((Math.abs(mc.x - original_x)) > bounds)
{
// Reverse the direction of travel:
x_inc == 5 ? x_inc = -5 : x_inc = 5;
}
// Ditto on the y axis:
if((Math.abs(mc.y - original_y)) > bounds)
{
y_inc == 5 ? y_inc = -5 : y_inc = 5;
}
}
// Start the loop:
addEventListener(Event.ENTER_FRAME, iterate);
This should get you started. I'm sure there are any number of other ways to do this with formal trig, but this has the benefit of being very simple, and just an extension of your existing method.

Related

Simple click and hit flash game

I am trying to create a simple game like whack a mole, what I want is for instead of mole make rectangle appear and disappear quickly on screen and the player has to click it more rectangles he click more his score increases.
I think it's a fairly simple game but my problem is:
How do I make the rectangles appear and disappear on screen at random position also at increasing speeds as the timer is decreasing? i.e speed of rectangles appearing and disappearing increases as the time reduces, there is a countdown time as player gets to play for 30 sec.
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
var inc:Number = 1;
var gogo:Timer = new Timer(inc*1000);
var val:Number = 30;
var counter = val;
var time2:Timer = new Timer(1000+speed);
var speed:Number = 50;
timee.text = counter;
box.addEventListener(MouseEvent.CLICK, st);
function st(event:MouseEvent):void
{
gogo.start();
time2.start();
}
gogo.addEventListener(TimerEvent.TIMER, res);
function res(ev:TimerEvent):void
{
if(counter == 0){
gogo.stop();
}else{
val = val - inc;
counter = val;
timee.text = counter;
}
}
stage.addEventListener(Event.ENTER_FRAME, yea);
function yea(e:Event):void{
speed += 50;
}
It seems to me that you need a math.random() method.
explained here
I'd personally have every successful 'hit' lower a "blinkSpeed" variable by either a set fraction of a second or a percentage and use that variable as my random input "max" number. That way it would decrease the time it could be available for automatically as they play.
Hello
Well, that wouldn't be much hard! Here's a clue.
Step1 : Create a new movieclip.
Step2 : Right click on your movie clip in the library. Click properties. Check the "Export for ActionScript" checkbox. Name the class MyEnemy (for example).
Step3 : Go to the frame and pull the Actions window.
Now put this code in it :
var mc:MyEnemy= new MyEnemy() // creates a instance of the movieclip.
addChild(mc); // adds the movie clip to the stage.
Step 4 : Now that we have added a new movieclip to the stage. To add it at a random x location in the stage, you need to make use of mc's x and y fields and the Math.random() function.
Example of Math.random() :
var randomThing:Number = Math.random() * 100 // returns a number between n, where 0 <= n < 100
Now, for example, we need both x and y values relative to the stage you must multiply Math.random() with the stage's width instead. As follows :
var randomX:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
var randomY:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
And! The magic final code would be :
mc.x = randomX;
mc.y = randomY;
Edit 1 : To add multiple "mc" movieclips to stage, we would use loops, do the follow :
stage.addEventListener(Event.ENTER_FRAME, doSimple);
function doSimple (ev:Event) {
var mc:MyEnemy = new MyEnemy();
var randomX:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
var randomY:Number = Math.random() * stage.stageWidth // returns a number between n, where 0 <= n < stage.stageWidth
for (var i:Number=1; i<=10;i++){ //i is a number, you can discard the 'i<=10' if you want an infinite loop.
addChild(mc);
}
mc.x = randomX;
mc.y = randomY;
trace("yes");
}
Conclusion : I look forward for your feedback!

Orbiting objects with equal spacing AS3

I have a monster that produces crystals. I want each crystal to orbit the monster, but when there is more than one crystal, I want them to orbit at an equal distance from each other. I've been trying to get this to work using two blocks of code I already have, but each one does something different and i need one block of code that does it all.
This block simply allows an object to orbit another:
orbitRadius = 110;
angle += orbitSpeed;
rad = (angle * (Math.PI / 180));
orbitX = monster.x + orbitRadius * Math.cos(rad);
orbitY = monster.y + orbitRadius * Math.sin(rad);
Here's a video of what it looks like:
https://www.youtube.com/watch?v=ACclpQBsjPo
This block of code arranges crystals around the monster based on the amount of crystals there are:
radius = 110;
angle = ((Math.PI * 2) / targetArray.length) * targetArray.indexOf(this);
orbitX = monster.x - (radius * Math.cos(angle));
orbitY = monster.y - (radius * Math.sin(angle));
And here's this video: https://www.youtube.com/watch?v=TY0mBHc2A8U
I do not know how to both space the crystals equally and make them circle around the monster at the same time. What needs to be done in order to achieve this?
1) Hierarchical way: put crystals into the same container so they spread equally (like you are doing on the second video) then rotate the container.
2) Math way.
Implementation:
public class Orbiter extends Sprite
{
// Pixels.
public var radius:Number = 100;
// Degrees per second.
public var speed:Number = 360;
public var items:Array;
public var lastTime:int;
public function start()
{
stop();
rotation = 0;
items = new Array;
lastTime = getTimer();
addEventListener(Event.ENTER_FRAME, onFrame);
}
public function stop():void
{
items = null;
removeEventListener(Event.ENTER_FRAME, onFrame);
}
public function onFrame(e:Event = null):void
{
var aTime:int = getTimer();
rotation += speed * (aTime - lastTime) / 1000;
lastTime = aTime;
for (var i:int = 0; i < items.length; i++)
{
// Get the object.
var anItem:DisplayObject = items[i];
// Get the object's designated position.
var aPos:Point = getPosition(i);
// Follow the position smoothly.
anItem.x += (aPos.x - anItem.x) / 10;
anItem.y += (aPos.y - anItem.y) / 10;
}
}
private function getPosition(index:int):Point
{
// Calculate the angle with regard to the present items amount.
var anAngle:Number = (rotation - 360 / items.length) * Math.PI / 180;
var result:Point = new Point;
// Figure the position with regard to (x,y) offset.
result.x = x + radius * Math.cos(anAngle);
result.y = y + radius * Math.sin(anAngle);
return result;
}
}
Usage:
var O:Orbiter = new Orbiter;
// Define the offset.
O.x = monster.x;
O.y = monster.y;
// Set radius and rotation speed.
O.radius = 110;
O.speed = 270;
// Enable the rotation processing.
O.start();
// Append items to orbit.
O.items.push(Crystal1);
O.items.push(Crystal2);
O.items.push(Crystal3);
You can change radius and speed any time, as well as add/remove items, thanks to motion smoothing that all will look equally fine.

Actionscript: Very slow movieclip

I am drawing a circular line to varying degrees. I wish the animation to last about 0.5 seconds. For reasons I can not work out its running very slowly.
What is weird is that if I skip the tween and call the function tweenToNext it renders instantly.
var degrees:int;
var posX:int = 102;
var posY:int = 102;
var rad:int = 100;
var mc:MovieClip = new MovieClip();
addChild(mc);
mc.graphics.lineStyle(5, 0xFF0000, 1);
mc.graphics.moveTo(posX, posY - rad)
mc.i = -Math.PI / 2;
tweenToNext();
function tweenToNext(per:Number = 360):void {
degrees += 1;
if (mc.i <= (3 * Math.PI / 2) && degrees < per) {
var x:Number = posX + Math.cos(mc.i) * rad;
var y:Number = posY + Math.sin(mc.i) * rad;
mc.graphics.lineTo(x, y);
mc.i += Math.PI / 180;
TweenLite.to(mc, 0.001, {onComplete:tweenToNext});
}
}
I have tried Timer and setTimeout but these produce the same slow speed.
Flash application runs on frame-to-frame basis: frame render - script execution - frame render - script execution - frame render - script execution - and so on. That also means that whatever smallest delay you're putting there, the next call will not happen before next script execution phase, basically, next frame. Thus - guess what - your circle drawing takes 360 frames. 12 seconds if you have 30 FPS, for example.
If you want to make something synchronize with the real time, you need a different approach. I didn't check if this works, but I hope you'll get the idea and fix the mistakes if any.
var degrees:int;
var posX:int = 102;
var posY:int = 102;
var rad:int = 100;
var mc:MovieClip = new MovieClip;
addChild(mc);
mc.graphics.lineStyle(5, 0xFF0000, 1);
mc.graphics.moveTo(posX, posY + rad);
// Now, magic time.
// Save time since app start (in milliseconds).
var startTime:int = getTimer();
// 1000 milliseconds = 1 second.
var drawingTime:int = 1000;
// Store the maximum degree to draw.
var degreeLimit:int = 360;
// Call it every frame.
mc.addEventListener(Event.ENTER_FRAME, onDraw);
function onDraw(e:Event):void
{
// Now we need to check how much time passes since last frame
// and update the drawing accordingly.
var timeProgress:Number = (getTimer() - startTime) / drawingTime;
var drawingProgress:Number = degrees / degreeLimit;
// When the drawing progress catches the time progress
// the loop will end. It will resume on the next frame.
while (drawingProgress < timeProgress)
{
degrees += 1;
// It's better than a property on target canvas,
// which could be Sprite or Shape, they wouldn't take random fields.
var anAngle:Number = degrees * Math.PI / 180;
var tox:Number = posX + Math.cos(anAngle) * rad;
var toy:Number = posY + Math.sin(anAngle) * rad;
mc.graphics.lineTo(tox, toy);
// We should know when to stop it.
if (dergees >= degreeLimit)
{
mc.removeEventListener(Event.ENTER_FRAME);
return;
}
// Update the drawing progress.
drawingProgress:Number = degrees / degreeLimit;
}
}

if a ball is hitting a box from every side, how to know which side hittest is true

i am creating a brick breaker game in which ball is hitting a square object. i want to change the direction of ball when he hit square object.
square is of 15 px. and
ball is of 10 px.
for example
if hits on the right side, Speed in x direction will reverse
if hits on the left side, Speed in x direction will reverse
if hits on the up side, Speed in y direction will reverse
if hits on the down side , Speed in y direction will reverse.
I tried it hard but found nothing. any help will be appreciated. here is the code:
import flash.events.*;
import flash.display.*;
stop();
// speed of ball in x and y
var speedx : Number = 10;
var speedy : Number = 10;
// start running
function begin_code (event:MouseEvent):void{
addEventListener(Event.ENTER_FRAME,move_ball);
stage.addEventListener(MouseEvent.MOUSE_DOWN,by_key);
addEventListener(Event.ENTER_FRAME,ht_mc);
start_game.alpha=0;
}
start_game.addEventListener(MouseEvent.CLICK, begin_code) ;
//start ball moving
function move_ball(e:Event):void{
Ball.x += speedx;
Ball.y += speedy;
Ball.rotation +=speedx;
var ballposition : Number = Ball.x -Hitbar.x;
var ballhitpercent : Number = (ballposition /(Hitbar.width-Ball.width));
if(Ball.x <= x1.x+Ball.width/2){
speedx = speedx *-1;
}
if(Ball.x >= x2.x-Ball.width/2){
speedx = speedx *-1;
}
if(Ball.y <=(55)){
speedy = speedy *-1;
}
else if (Ball.y >= stage.stageHeight-Ball.height){
speedy = speedy *-1;
trace("hit y");
}
//start ball angle
else if(Ball.hitTestObject(Hitbar)){
speedx = ballhitpercent*10;
speedy = speedy *-1;
}
}
//start hitbar moving
// code for keys!!!!!!!!!
var distance : Number = 0;
function by_key(e:MouseEvent):void{
if (mouseX>Hitbar.x){
distance = (mouseX-Hitbar.x);
Hitbar.x += distance;
}
else if (mouseX<Hitbar.x){
distance = (Hitbar.x-mouseX);
Hitbar.x -= distance;
}
}
//restart
function by_key_up(e:MouseEvent):void{
}
stage.addEventListener(MouseEvent.MOUSE_UP, by_key_up);
function pLimiter(e:Event):void{
Hitbar.y = 406.2;
if (Hitbar.hitTestObject(x1)){
Hitbar.x = 32.6;
}
if (Hitbar.hitTestObject(x2)){
Hitbar.x = 287.55;
}
}
addEventListener(Event.ENTER_FRAME, pLimiter);
// Bricks set hit
var pickup:Array =new Array();
for (var i = 0;i<numChildren;i++){
if(getChildAt(i) is abc){
pickup.push(getChildAt(i));
}
}
function hittest_box(e:Event):void{
for (var f = 0;f<pickup.length;f++){
if (Ball.hitTestObject(pickup[f])){
if(pickup[f].parent) {
pickup[f].parent.removeChild(pickup[f]);
}
}
}
}
function ht_mc(e:Event):void{
for (var j = 0;j<pickup.length;j++){
var ball_pos_x: Number = ( Ball.x - pickup[j].x);
var ball_pos_y: Number = ( Ball.y - pickup[j].y);
if( ball_pos_x < 11.5 && ball_pos_x > -11.5){
speedy = speedy *-1;
trace("box y");
addEventListener(Event.ENTER_FRAME,hittest_box);
}
else if ( ball_pos_y < 11.5 && ball_pos_y > -11.5){
speedx = speedx *-1;
trace("boxx");
addEventListener(Event.ENTER_FRAME,hittest_box);
}
}
}
edit
To turn my two point example into a one-ball-moving version, the main thing you need to do is to declare the p1 then move the ball and then declare p2.
p1.x = ball.x;
p1.y = ball.y;
moveBall();
p2.x = ball.x;
p2.y = ball.y;
Now you have p1 and p2. Run the calculations as below to find which brick is hit and which side was hit.
A few more calculations will be needed (and you'll have to loop through this whole thing for every collision that occurs in between frames). But I can't post all that here.
Original Answer
My answer is meant to be able to be used in any situation in which you have a point object that is moving and needs to detect when it collides with sides of a square. Simple hitTest solutions are inadequate because a point object moving fast enough could "jump over" a corner. Additionally, if the walls of the object are infinitely thin, there couldn't easily be a "collision", so what we really want to calculate is whether or not the point passed through a wall during the last tick. Also, if the ball is going fast enough to jump over an entire brick, you'll get collision with bricks that are surrounded by other bricks. This method fixes that. Here we go...
My illustration shows the basic method I'm using. It breaks down like this:
define end points for each line segment (corners of block and 2 positions of ball)
check first if ball bounding box and block overlap with simple hit test type of conditional statement
if they do, determine which edge the ball passed through by comparing the 3 angles as shown in the diagram (angle 1 just has to be between angle 2 and 3)
then determine which of the points that make up the lines that the ball passed through during the last frame transition and determine which of these points is nearest the where the ball was on the previous frame (this will be a point contained in the line that the ball will hit!)
lastly, determine which line contains that point
There could certainly be some optimizations added, but this works like a charm for what it is.
This code will add a bunch of LineTestBlock objects to the stage as well as 2 Point objects associated with circles. Move the Points such that 1 will represent the first frame location of the ball, and the second Point can be dragged to a place representing where the ball will be on the 2nd frame. You will see that wherever the line between these passes through, the closest edge of a block will light up.
And here is the code. It is a main document class named BlockDoc and a LineTestBlock class.
package {
import flash.display.MovieClip;
import flash.geom.Point;
import flash.events.Event;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.geom.Rectangle;
import flash.display.Shape;
public class BlockDoc extends MovieClip {
// this holds the circle handles (the actual sprite objects)
var cArray = new Array();
// this holds all the bricks
var brickArray = new Array();
//ball position in frame 1
//these are just point objects. p1 will need to represent where the ball was on the last frame
var p1:Point = new Point();
// and p2 will need to be where the ball is "going" to be on the next frame
// although, if it hits something, the ball will not end up p2 on the next screen update, obviously
var p2:Point = new Point();
// just an array used for putting the 1 and 2 on the handles
// you won't eventually need this, of course
var pointNameArray:Array = new Array();
// this is the Sprite object that holds the rectangle
// which describes the bounding box made by the ball in the two frames
var recASprite:Sprite = new Sprite();
// rec is the Sprite that holds the bricks, or you could just put the bricks on the stage. I like having them in a container so I can remove them all or move them all or change them all just by changing the container
var rec:Sprite = new Sprite();
// lines is the Shape object that all the bricks edge lines are drawn to
var lines:Shape = new Shape();
// mainLine is the Shape object that the line connecting the two ball positions is drawn to
var mainLine:Shape = new Shape();
// you won't need either of these lines in your version
public function BlockDoc() {
// just a background so you can see the alpha better.
// you won't need this in yours
var bkgnd:Sprite = new Sprite();
bkgnd.graphics.lineStyle();
bkgnd.graphics.beginFill(0x0);
bkgnd.graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
bkgnd.graphics.endFill();
addChild(bkgnd);
makeBricks();
createArray();
addChild(mainLine);
addChild(lines);
createHandles(); // creates the circle handles
addEventListener(Event.ENTER_FRAME,gameTick);
}
private function drawLine(container:Shape,p1:Point,p2:Point,color:uint=0xffbbee):void{
container.graphics.clear();
container.graphics.lineStyle(2,color);
container.graphics.moveTo(p1.x,p1.y);
container.graphics.lineTo(p2.x,p2.y);
}
private function makeBricks():void{
// creates rectangle object defined by frame 1 and 2 of ball position
updateBallBoundingBox(recASprite,p1,p2);
addChild(rec); // the parent for all bounding boxes
makeSimpleRectangle(recASprite); // this is the bounding box for the 2 positions of the ball
// these are the bricks
for (var i:int = 0; i < 5; i++){
for (var j: int = 0; j < 3; j++){
var b: LineTestBlock = new LineTestBlock(50,25);
rec.addChild(b);
b.alpha = .5;
brickArray.push(b);
b.x = 100 * i + 50;
b.y = 80 * j +100;
// top left corner of brick
b._p1.x = b.x;
b._p1.y = b.y;
// top right corner of brick
b._p2.x = b.x + b.width;
b._p2.y = b.y;
// bottom right corner of brick
b._p3.x = b.x + b.width;
b._p3.y = b.y + b.height;
// bottom left corner of brick
b._p4.x = b.x;
b._p4.y = b.y + b.height;
}
}
}
private function makeSimpleRectangle(rect:Sprite,color:uint = 0x0):void{
rect.graphics.lineStyle(0,0x0,0);
rect.graphics.beginFill(color,.5);
rect.graphics.drawRect(0,0,50,50);
rect.graphics.endFill();
}
private function updateBallBoundingBox(rec:Sprite,p1:Point,p2:Point):void{
rec.x = Math.min(p2.x,p1.x);
rec.y = Math.min(p2.y,p1.y);
rec.width = Math.max(p2.x,p1.x)-rec.x;
rec.height = Math.max(p2.y,p1.y)-rec.y;
}
private function checkIntersection(A1:Point,A2:Point,B1:Point,B2:Point):Boolean{
// adjusts the current "bounding box" using the position 1 and position 2 of the ball
updateBallBoundingBox(recASprite,A1,A2);
// if bounding boxes hit
// define the angles to be used (further optimization can easily be done here by
// only checking the 2 possible sides of contact, or using a look up table
// instead of using Math.atan2)
// angle between position 1 and 2 of the ball
var A1_A2_r:Number = Math.atan2(A2.y - A1.y,A2.x - A1.x);
// angle between position 1 and one corner
var A1_B1_r:Number = Math.atan2(B1.y - A1.y,B1.x - A1.x);
// angle between position 1 and the second corner
var A1_B2_r:Number = Math.atan2(B2.y - A1.y,B2.x - A1.x);
// and if angle from position 1 to position 2 is between angle from one corner to the other corner...
if (A1_A2_r > A1_B1_r && A1_A2_r < A1_B2_r){
// return true
return true;
} else {
return false;
}
}
private function gameTick(e:Event):void{
// p1 and p2 are the point objects that describe frame 1 and frame 2 of the moving ball
// for actual moving ball:
//p1.x = Ball.x;
//p1.y = Ball.y;
//move_ball();
//p2.x = Ball.x;
//p2.y = Ball.y;
//or use this for experimenting with the handles
//p1.x = cArray[0].x;
//p1.y = cArray[0].y;
//p2.x = cArray[1].x;
//p2.y = cArray[1].y;
// draw line between position 1 and 2
drawLine(mainLine,p1,p2);
checkCollision();
}
private function checkCollision():void{
// clears previously drawn lines on bricks
lines.graphics.clear();
var xDir:int;
var yDir:int;
// determine direction of ball (you probably already have this as a variable somewhere so probably don't need this
if (p1.x < p2.x){
xDir = 1;
} else if (p1.x > p2.x){
xDir = -1;
} else {
xDir = 0;
}
if (p1.y < p2.y){
yDir = 1;
} else if (p1.y > p2.y){
yDir = -1;
} else {
yDir = 0;
}
// this array will hold all the Point objects involved in true collision
var arr:Array = new Array();
var arrLines:Array = new Array();
var nearestDistance:Number = 2000; // change this to something sensible (maybe the speed of the ball...);
var nearestPoint:Point = new Point(); // change this to something sensible
// first check bounding boxes
for (var i:int = 0; i < brickArray.length; i++){
var b:LineTestBlock = brickArray[i];
var a:Sprite = recASprite;
updateBallBoundingBox(a,p2,p1);
// this is just a bounding box hittest, and hitTestObject used to work, but I broke something, so I have to do it like this now
if (a.x+a.width > b.x && a.y + a.height > b.y && a.x < b.x+b.width && a.y<b.y+b.height){
// now that we know the rectangles overlap, we check the two sides that are hittable
// instead of xDir and yDir just use your variable or property that holds the x and y speeds;
// I use xDir and yDir because my example doesn't actually use motion.
if (xDir > 0){
if (checkIntersection(p1,p2,b._p1,b._p4)){
// ball hits left wall of brick
//drawLine(lines,b._p1,b._p4,0x00ffee);
// instead of drawLine above, put in your function for collision of left wall
arr.push(b._p1,b._p4);
arrLines.push({p1:b._p1,p2:b._p4,brick:brickArray[i]});
// that last array there simply holds an object with 3 properties
// for use later in comparing what should actually receive the hit.
}
if (yDir > 0) {
if (checkIntersection(p2,p1,b._p1,b._p2)){
// ball hits top wall of brick
arr.push(b._p1,b._p2);
arrLines.push({p1:b._p1,p2:b._p2,brick:brickArray[i]});
}
} else if (yDir < 0){
if(checkIntersection(p2,p1,b._p3,b._p4)){
// ball hits bottom wall of brick
arr.push(b._p3,b._p4);
arrLines.push({p1:b._p3,p2:b._p4,brick:brickArray[i]});
}
}
} else if (xDir < 0){
if(checkIntersection(p2,p1,b._p2,b._p3)){
// ball hits right edge of brick
arr.push(b._p2,b._p3);
arrLines.push({p1:b._p2,p2:b._p3,brick:brickArray[i]});
}
if (yDir > 0) {
if (checkIntersection(p2,p1,b._p1,b._p2)){
// ball hits top edge of brick
arr.push(b._p1,b._p2);
arrLines.push({p1:b._p1,p2:b._p2,brick:brickArray[i]});
}
} else if (yDir < 0){
if(checkIntersection(p2,p1,b._p3,b._p4)){
// ball hits bottom edge of brick
arr.push(b._p3,b._p4);
arrLines.push({p1:b._p3,p2:b._p4,brick:brickArray[i]});
}
}
}
}
}
for (var i:int = 0; i < brickArray.length; i++){
brickArray[i].alpha = 0.3;
}
for (var i:int = 0; i < arr.length; i++){
var d:Number = dist(arr[i],p1);
if(d < nearestDistance){
nearestDistance = d;
nearestPoint = arr[i];
}
}
for (var j:int = 0; j < arrLines.length; j++){
if (arrLines[j].p1 == nearestPoint){
drawLine(lines,nearestPoint,arrLines[j].p2);//THIS IS THE EDGE THAT GETS HIT!!
arrLines[j].brick.alpha = 0.7; // THIS IS THE BRICK THAT GETS HIT!!
} else if (arrLines[j].p2 == nearestPoint){
drawLine(lines,nearestPoint,arrLines[j].p1); //THIS IS THE EDGE THAT GETS HIT!!
arrLines[j].brick.alpha = 0.7; // THIS IS THE BRICK THAT GETS HIT!!
}
}
}
private function dist(p1:Point, p2:Point):Number{
var dx:Number = p1.x - p2.x;
var dy:Number = p1.y - p2.y;
return Math.sqrt(dx * dx + dy * dy);
}
private function createHandles():void{
// these are the circle handles for moving the balls around
for (var i:int = 0; i < 2; i++){
var c:Sprite = new Sprite();
var tf:TextField = new TextField();
c.graphics.lineStyle();
c.graphics.beginFill(0x5555ff+0x5555*i);
c.graphics.drawCircle(0,0,10);
c.graphics.endFill();
addChild(c);
c.addChild(tf);
c.x = 50*i+50;
c.y = 50;
c.addEventListener(MouseEvent.MOUSE_DOWN,dragObject);
c.addEventListener(MouseEvent.MOUSE_UP,dragStopObject);
cArray.push(c);
tf.height = 20;
tf.width = 10;
tf.text = pointNameArray[i];
tf.x = -5;
tf.y = -10;
tf.mouseEnabled = false;
}
}
private function createArray():void{
// just used for putting the fancy 1 and 2 on the handles
pointNameArray = ["1","2"];
}
private function radsFromPoints(p1:Point,p2:Point):Number{
return Math.atan2(p1.y - p2.y, p1.x - p2.y);
}
private function dragObject(me:MouseEvent):void{
me.target.startDrag(true);
}
private function dragStopObject(me:MouseEvent):void{
me.target.stopDrag();
}
}
}
and the LineTestBlock class:
package {
import flash.display.*;
import flash.text.*;
import flash.geom.Point;
public class LineTestBlock extends Sprite{
var _p1:Point = new Point();
var _p2:Point = new Point();
var _p3:Point = new Point();
var _p4:Point = new Point();
public function LineTestBlock(w:Number,h:Number) {
var s:Sprite = new Sprite();
s.graphics.lineStyle();
s.graphics.beginFill(0xff5599,0.5);
s.graphics.drawRect(0,0,w,h);
s.graphics.endFill();
addChild(s);
}
}
}
Obviously the stuff about lines and alpha are just for display purposes. You'll be able to erase a bunch of this code and will need to replace some of it with actual bounce functions.
One thing to keep in mind is how you deal with the bouncing off of the ball. You can't simply reverse the x or y speed of the ball, as you might think because then it is inside of a brick, or on the opposite side of the brick trying to come back out, which will work, but could look funny and actually allow the ball access to bricks that should be off limits because the are surrounded by other bricks. But I've already answered the question at hand, so I'll stop there.

Calculate a trajectory for an object, when given an angle and a velocity in flash

I am trying to launch a cannonball from a cannon and have it follow a realistic path. The angle of fire changes depending on the orientation of the cannon (automatically orientates to mouse pointer). So what I'm trying to figure out, is how to move a cannonball along a parabolic path, when given an angle, and a set velocity.
I've read that this can be done without complicated trigonometry (never listened to it in highschool), and can be calculated simply by adding gravity to the yVelocity every tick. However, at this moment, I don't know how to calculate the initial yVelocity (again, depending on cannon orientation).
You can see the current animation here: http://kate.ict.op.ac.nz/~welfajw1/portfolio/videos/task3-assignment2.swf
This is all done in AS3, and the code I have is as follows:
Main timeline code:
import flash.display.*;
import flash.events.*;
import flash.geom.*;
var cannonball:ball_mc;
var angleDegree;
myCannon.addEventListener(Event.ENTER_FRAME, cannonEnterFrame);
function cannonEnterFrame(pEvt)
{
var mc = myCannon;
var mg = myCannon.myGun;
//find angle for orientation
var angleRadian = Math.atan2(mouseY - mc.y, mouseX - mc.x);
//convert to degrees
angleDegree = angleRadian * 180 / Math.PI;
//limit rotation
if(angleDegree > -63 && angleDegree < 20)
mg.rotation = angleDegree;
}
stage.addEventListener(Event.ENTER_FRAME, stageRefresh);
function stageRefresh(pEvt)
{
if (cannonball)
{
//move every "tick"
cannonball.move();
}
}
stage.addEventListener(MouseEvent.CLICK, mouseClicked);
function mouseClicked(pEvt)
{
//starting position of the ball
cannonball = new ball_mc(100, 475);
//SEND IN INITIAL x, y VELOCITIES
cannonball.fire(20, angleDegree);
//add to stage
stage.addChild(cannonball);
}
ball_mc code:
package
{
import flash.display.MovieClip;
import flash.sensors.Accelerometer;
import flashx.textLayout.formats.Float;
public class ball_mc extends MovieClip
{
//constant gravity
public static const g:Number = 2;
//starting velocities
private var ux:Number;
private var uy:Number;
public function ball_mc(startX:int, startY:int)
{
x = startX;
y = startY;
}
public function fire(vx:Number, vy:Number):void
{
ux = vx;
uy = vy;
}
public function move():void
{
//distance moved in x dir
var sx:Number = ux;
//new velocity in y dir
var vy:Number = uy + g;
//distance moved in y dir
var sy:Number = uy + g/2;
//apply movement
x += sx;
y += sy;
//save new y velocity
uy = vy;
}
}
}
You need a little bit of physics.
Initial speed must be calculated by using some criteria that you add on your own. One example is to calculate initial speed by using the distance between the mouse and the cannon, at the time the mouse is pressed. If the distance is greater the projectile will have a bigger speed, and if the distance is smaller the projectile will have smaller speed.
The you add an Event Listener with type ENTER_FRAME.
I guess it's 2 dimensional animation so you have to find the current x and y at any point in time.
Here's a little bit of code:
var TimeperFrame:Number = 1/fps //fps is not a constant, here you should add a number, a value that you previously added in fla. document properties. I usualy use 60 fps
var Time:Number = 0;
addEventListener(ENTER_FRAME, movingCannonBall);
function movingCannonBall(e:Event):void
{
Time += TimeperFrame;
}
Now here's the equitation for trajectory of projectile.
x = xo + vxo·t
y = yo + vyo·t - 0.5·g·t^2
yo = initial height of your cannon ball
vyo = initial y velocity; vyo = vo·sin θ
t = time passed, we conrol that by upper code
g = acceleration (9,81 m/s^2) at Earth's surface
xo = initial distance for the start
vxo = initial x velocity; vxo = vo·cos θ
Now in the upper code we add these equitations and it should look like this:
var TimeperFrame:Number = 1/fps
var Time:Number = 0;
var initx: Number = cannonball.x;
var inity: Number = cannonball.y;
var initVelocity: Number = (you define initial Velocity by your criteria)
var G: Number = 9.81;
addEventListener(ENTER_FRAME, movingCannonBall);
function movingCannonBall(e:Event):void
{
Time += TimeperFrame;
cannonball.x = initx + Math.cos(angle) * initVelocity * Time;
cannonball.y = inity + Math.sin(angle) * initVelocity * Time - G * Time * Time * 0.5
}
This should work. I have use this code many times and it's effiecient and also it's simple.