I have this code that generates circles and makes them float within the boundaries of the stage. Although it stays in the stage it also has some give and let's the circles push through a small amount which I like.
Is it possible to do this but with a custom shape and have the circles confined inside this shape?
Here is the code I have:
//number of balls
var numBalls:uint = 200;
var defaultBallSize:uint = 8;
var colors:Array = [0x79B718, 0x2D91A8, 0xB019BC, 0xF98715, 0xDB1616];
//init
makeDots();
function makeDots():void {
//create desired number of balls
for (var ballNum:uint=0; ballNum<numBalls; ballNum++){
var c1:Number = randomColor();
var c2:Number = randomColor();
//create ball
var thisBall:MovieClip = new MovieClip();
thisBall.graphics.beginFill(c1);
//thisBall.graphics.lineStyle(defaultBallSize, 0);
thisBall.graphics.drawCircle(defaultBallSize, defaultBallSize, defaultBallSize);
thisBall.graphics.endFill();
addChild(thisBall);
//coordinates
thisBall.x = Math.random() * stage.stageWidth;
thisBall.y = Math.random() * stage.stageHeight;
//percieved depth
thisBall.ballNum = ballNum;
thisBall.depth = ballNum/numBalls;
thisBall.scaleY = thisBall.scaleX =
////thisBall.alpha =
ballNum/numBalls;
//velocity
thisBall.vx = 0;
thisBall.vy = 0;
thisBall.vz = 0;
//ball animation
thisBall.addEventListener(Event.ENTER_FRAME, animateBall);
}
}
var dampen:Number = 0.90;
var maxScale:Number = 1.3;
var minScale:Number = .3;
var maxAlpha:Number = 1.3;
var minAlpha:Number = .3;
function animateBall(e:Event):void{
var thisBall:Object = e.target;
//apply randomness to velocity
thisBall.vx += Math.random() * 0.2 - 0.1;
thisBall.vy += Math.random() * 0.2 - 0.1;
thisBall.vz += Math.random() * 0.002 - 0.001;
thisBall.x += thisBall.vx;
thisBall.y += thisBall.vy;
//thisBall.scaleX = thisBall.scaleY += thisBall.vz;
//thisBall.alpha += thisBall.vz;
thisBall.vx *= dampen;
thisBall.vy *= dampen;
thisBall.vz *= dampen;
if(thisBall.x > stage.stageWidth) {
thisBall.x = 0 - thisBall.width;
}
else if(thisBall.x < 0 - thisBall.width) {
thisBall.x = stage.stageWidth;
}
if(thisBall.y > stage.stageHeight) {
thisBall.y = 0 - thisBall.height;
}
else if(thisBall.y < 0 - thisBall.height) {
thisBall.y = stage.stageHeight;
}
if (thisBall.scaleX > maxScale){
thisBall.scaleX = thisBall.scaleY = maxScale;
}
else if (thisBall.scaleX < minScale){
thisBall.scaleX = thisBall.scaleY = minScale;
}
if (thisBall.alpha > maxAlpha){
thisBall.alpha = maxAlpha;
}
else if (thisBall.alpha < minAlpha){
thisBall.alpha = minAlpha;
}
}
function randomColor():uint
{
return colors[int(Math.random()*colors.length)];
}
Code credit:
Originally from here: Circle Cube
Additional help here: Random colour within a list of pre-defined colours
Yes, you can. What is happening is that when each circle moves, it is checked to see if it is within the stage bounds on the x and y axis and is 'corrected' if it goes out. You can modify that part of the code that determines this to check if a circle is within your custom shape or not.
The complexity of this will depend on your custom shape as well as the method to go about detecting if a circle/object is within your custom shape.
The 'easiest custom shape' you could try would be a rectangle or square, since the stage is already a big rectangle. To start this, look through your given code to find the lines of code that limit the x and y position of the stage dimensions and change them to the dimensions of your custom rectangle/square. You may have to add in position offsets if your custom shape rectangle/square does not originate from 0, 0 like the stage. I suggest factoring this part out (which is actually basic collision detection) into a method if you want to experiment with other shapes.
Edit
I edited my answer to include the original code reworked to use a random square as the custom shape -the easiest shape to try as mentioned in my original answer. Hopefully you can compare the two and see the changes I made to try and figure out the logic behind it.
For a circle, or any other totally random shape, it would be a bit more difficult, but same idea/concept.
//number of balls
var numBalls:uint = 200;
var defaultBallSize:uint = 8;
var colors:Array = [0x79B718, 0x2D91A8, 0xB019BC, 0xF98715, 0xDB1616];
// new custom shape bounds, a square that is 200, 200 px and is at 175, 100 on the stage
var customSquare:Rectangle = new Rectangle(175, 100, 200, 200);
//init
makeDots();
function makeDots():void {
//create desired number of balls
for (var ballNum:uint=0; ballNum < numBalls; ballNum++){
var c1:Number = randomColor();
var c2:Number = randomColor();
//create ball
var thisBall:MovieClip = new MovieClip();
thisBall.graphics.beginFill(c1);
//thisBall.graphics.lineStyle(defaultBallSize, 0);
thisBall.graphics.drawCircle(defaultBallSize, defaultBallSize, defaultBallSize);
thisBall.graphics.endFill();
addChild(thisBall);
//coordinates - this part of the code is setting the initial positions of the circles based on the stage size
//thisBall.x = Math.random() * stage.stageWidth;
//thisBall.y = Math.random() * stage.stageHeight;
//
// changed so they use the "customSquare" rectangle instead, note that the custom shape has an x and y pos now that doesn't start at 0 (unlike the stage)
thisBall.x = (Math.random() * customSquare.width) + customSquare.x;
thisBall.y = (Math.random() * customSquare.height) + customSquare.y;
//percieved depth
thisBall.ballNum = ballNum;
thisBall.depth = ballNum / numBalls;
thisBall.scaleY = thisBall.scaleX = ballNum / numBalls;
//velocity
thisBall.vx = 0;
thisBall.vy = 0;
thisBall.vz = 0;
//ball animation
thisBall.addEventListener(Event.ENTER_FRAME, animateBall);
}
}
var dampen:Number = 0.90;
var maxScale:Number = 1.3;
var minScale:Number = .3;
var maxAlpha:Number = 1.3;
var minAlpha:Number = 0.3;
function animateBall(e:Event):void{
var thisBall:Object = e.target;
//apply randomness to velocity
/*thisBall.vx += Math.random() * 0.2 - 0.1;
thisBall.vy += Math.random() * 0.2 - 0.1;
thisBall.vz += Math.random() * 0.002 - 0.001;*/
// increased velocity ranges to add more speed to see the effects easier
thisBall.vx += Math.random() * 1.2 - 0.6;
thisBall.vy += Math.random() * 1.2 - 0.6;
thisBall.vz += Math.random() * 0.012 - 0.006;
thisBall.x += thisBall.vx;
thisBall.y += thisBall.vy;
//thisBall.scaleX = thisBall.scaleY += thisBall.vz;
//thisBall.alpha += thisBall.vz;
thisBall.vx *= dampen;
thisBall.vy *= dampen;
thisBall.vz *= dampen;
// =====================================================================================================================================
// this part of the code is determining if each ball is going outside of the bounds of the stage and repositioning them if they are
// this part is the 'collision detection', changed to use the bounds of the "customSquare" rectangle instead
//
// this part is detecting the position going out of bounds along the X axis
/*if(thisBall.x > stage.stageWidth) {
thisBall.x = 0 - thisBall.width;
} else if(thisBall.x < 0 - thisBall.width) {
thisBall.x = stage.stageWidth;
}*/
if(thisBall.x > (customSquare.width + customSquare.x)) {
thisBall.x = customSquare.x - thisBall.width;
} else if(thisBall.x < customSquare.x - thisBall.width) {
thisBall.x = customSquare.width + customSquare.x;
}
// this part is detecting the position going out of bounds along the Y axis
/*if(thisBall.y > stage.stageHeight) {
thisBall.y = 0 - thisBall.height;
} else if(thisBall.y < 0 - thisBall.height) {
thisBall.y = stage.stageHeight;
}*/
if(thisBall.y > (customSquare.height + customSquare.y)) {
thisBall.y = customSquare.y - thisBall.height;
} else if(thisBall.y < customSquare.y - thisBall.height) {
thisBall.y = customSquare.height + customSquare.y;
}
// =====================================================================================================================================
if (thisBall.scaleX > maxScale){
thisBall.scaleX = thisBall.scaleY = maxScale;
} else if (thisBall.scaleX < minScale){
thisBall.scaleX = thisBall.scaleY = minScale;
}
if (thisBall.alpha > maxAlpha){
thisBall.alpha = maxAlpha;
} else if (thisBall.alpha < minAlpha){
thisBall.alpha = minAlpha;
}
}
function randomColor():uint{ return colors[int(Math.random()*colors.length)]; }
Assuming all you are asking is for a border around the confined area, You could do something like:
var container:MovieClip = new MovieClip;
container.graphics.lineStyle(1,0,1);
container.graphics.drawRect(0, 0, container.width, container.height);
container.graphics.endFill();
addChild(container);
And Replace :
addChild(thisBall);
With :
container.addChild(thisBall);
From animating in flash I can tell you the flash-way of doing something like this is through layer masks. This should is possible in code too. Something loosely like this:
addChild(thisBall);
var layermask:Shape=new Shape();
addChild(layermask);
thisBall.mask=maskingShape;
Related
I am developing a game in actionscript 3.0 (adobe flash) similar to this https://www.tvokids.com/preschool/games/caterpillar-count. I have the code for dragging the head of the snake in the direction of the mouse. However, I do not know how do I add the body of the snake and make it follow the path of the head. Following is my code to drag movieclip in the direction of the mouse :
var _isActive = true;
var _moveSpeedMax:Number = 1000;
var _rotateSpeedMax:Number = 15;
var _decay:Number = .98;
var _destinationX:int = 150;
var _destinationY:int = 150;
var _dx:Number = 0;
var _dy:Number = 0;
var _vx:Number = 0;
var _vy:Number = 0;
var _trueRotation:Number = 0;
var _player;
var i;
createPlayer();
stage.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
function createPlayer():void{
_player = new head();
_player.x = stage.stageWidth / 2;
_player.y = stage.stageHeight / 2;
stage.addChild(_player);
}
function onDown(e:MouseEvent):void{
_isActive = true;
stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.addEventListener(MouseEvent.MOUSE_UP, onUp);
}
function onMove(e:MouseEvent):void{
updatePosition(_player);
updateRotation(_player);
}
function onUp(e:MouseEvent):void{
_isActive = false;
stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, onUp);
}
function updatePosition(mc):void
{
// check if mouse is down
if (_isActive)
{
// update destination
_destinationX = stage.mouseX;
_destinationY = stage.mouseY;
// update velocity
_vx += (_destinationX - mc.x) / _moveSpeedMax;
_vy += (_destinationY - mc.y) / _moveSpeedMax;
}
else
{
// when mouse is not down, update velocity half of normal speed
_vx += (_destinationX - mc.x) / _moveSpeedMax * .25;
_vy += (_destinationY - mc.y) / _moveSpeedMax * .25;
}
// apply decay (drag)
_vx *= _decay;
_vy *= _decay;
// if close to target, slow down turn speed
if (getDistance(_dx, _dy) < 50)
{
_trueRotation *= .5;
}
// update position
mc.x += _vx;
mc.y += _vy;
}
function updateRotation(mc):void
{
// calculate rotation
_dx = mc.x - _destinationX;
_dy = mc.y - _destinationY;
// which way to rotate
var rotateTo:Number = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (rotateTo > mc.rotation + 180) rotateTo -= 360;
if (rotateTo < mc.rotation - 180) rotateTo += 360;
// ease rotation
_trueRotation = (rotateTo - mc.rotation) / _rotateSpeedMax;
// update rotation
mc.rotation += _trueRotation;
}
function getDistance(delta_x:Number, delta_y:Number):Number
{
return Math.sqrt((delta_x*delta_x)+(delta_y*delta_y));
}
function getRadians(delta_x:Number, delta_y:Number):Number
{
var r:Number = Math.atan2(delta_y, delta_x);
if (delta_y < 0)
{
r += (2 * Math.PI);
}
return r;
}
function getDegrees(radians:Number):Number
{
return Math.floor(radians/(Math.PI/180));
}
The script below will not miraculously work on its own, however it has all the logic you need, well-explained. It makes a chain of any length follow its head by certain rules. I used the same principle here many years ago: http://delimiter.ru/games/25-lines/alone.html
// This one will represent the Mouse position.
var Rat:Sprite = new Sprite;
// The ordered list of chain elements.
// It all starts with the Mouse.
var Snake:Array = [Rat];
// Call this one each time you want to
// extend the snake with the piece of tail.
function addTail(aPiece:DisplayObject):void
{
// Get the last snake element.
var lastPiece:DisplayObject = Snake[Snake.length - 1];
// Sync the tail coordinates.
aPiece.x = lastPiece.x;
aPiece.y = lastPiece.y;
// Add the new piece to the snake.
Snake.push(aPiece);
}
// Add the pre-defined head as the first element.
addTail(SnakeHead);
// Now start following the Mouse.
addEventListener(Event.ENTER_FRAME, onFrame);
// Fires every frame and adjusts the whole snake, if needed.
function onFrame(e:Event):void
{
// Sync the attractor point with the Mouse.
Rat.x = mouseX;
Rat.y = mouseY;
// Now lets make each piece follow the previous piece,
// one by one, starting from the head, down to the tail.
for (var i:int = 1; i < Snake.length; i++)
{
followOne(Snake[i - 1], Snake[i]);
}
}
function followOne(A:DisplayObject, B:DisplayObject):void
{
// Think of these values as of vector
// pointing from B position to A position.
var dx:Number = A.x - B.x;
var dy:Number = A.y - B.y;
// Figure out the distance between the given pieces.
var aDist:Number = Math.sqrt(dx * dx + dy * dy);
// Do nothing if pieces are closer than 20 px apart.
// You can change this value to make snake shorter or longer.
if (aDist < 20)
{
return;
}
// This literally means "eat one tenth of the distance
// between me and the previous piece". If you want pieces
// to follow each other with more vigor, reduce this value,
// if you want the whole snake to slither smoothly, increase it.
B.x += dx / 10;
B.y += dy / 10;
// Rotate the B piece so it would look right into A's direction.
// Well, unless your pieces are round and look all the same.
B.rotation = Math.atan2(dy, dx) * 180 / Math.PI;
}
I'm trying to make a game, following this tutorial.
The issue comes from the fact that I am using ActionScript 3.0 whereas the tutorial was written using ActionScript 2.0.
Regarding the sight of the enemy, I have turned this code:
onClipEvent (enterFrame) {
dist_x = _root.hero._x-_x;
dist_y = _root.hero._y-_y;
dist = Math.sqrt(dist_x*dist_x+dist_y*dist_y);
angle = Math.atan(dist_y/dist_x)/(Math.PI/180);
if (dist_x<0) {
angle += 180;
}
if (dist_x>=0 && dist_y<0) {
angle += 360;
}
wall_collision = 0;
for (x=1; x<=dist; x++) {
point_x = _x+x*Math.cos(angle*Math.PI/180);
point_y = _y+x*Math.sin(angle*Math.PI/180);
if (_root.wall.hitTest(point_x, point_y, true)) {
wall_collision = 100;
break;
}
}
_root.line._x = _x;
_root.line._y = _y;
_root.line._rotation = angle;
_root.line._alpha = 100-wall_collision;
}
Into that:
// calculate rotation based on target
_dx = this.x - _root.hero.x;
_dy = this.y - _root.hero.y;
// which way to rotate
_rotateTo = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (_rotateTo > barrel.rotation + 90) _rotateTo -= 360;
if (_rotateTo < barrel.rotation - 90) _rotateTo += 360;
// ease rotation
_trueRotation = (_rotateTo - barrel.rotation) / _rotateSpeedMax;
// update rotation
barrel.rotation += _trueRotation;
wall_collision = 0;
OuterLoop: for (var xi=1; xi<=_dx; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_rotateTo);
var point_y:Number = this.y + xi*Math.sin(_rotateTo);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation += _trueRotation;
_root.sight.alpha = 100 - wall_collision;
But the it does not work.
The rotation do work fine, but the whole "alpha = 0 if player is behind a wall" does not work.
Please help me resolving the issue.
Try the following:
// calculate rotation based on target
_dx = _root.hero.x-this.x;
_dy = _root.hero.y-this.y;
// The full distance is missing from your AS3 code
_dist = Math.sqrt(_dx*_dx+_dy*_dy);
// Return the old good approach for finding angle
angle = Math.atan(_dy/_dx)/(Math.PI/180);
if (_dx<0) {
_angle += 180;
}
if (_dx>=0 && _dy<0) {
_angle += 360;
}
wall_collision = 0;
OuterLoop: for (var xi=1; xi<=_dist; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_angle*Math.PI/180);
var point_y:Number = this.y + xi*Math.sin(_angle*Math.PI/180);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation = _angle;
// Alpha changed from [0, 100] scale to [0, 1] scale.
_root.sight.alpha = (100 - wall_collision) * 0.01;
Information on alpha in ActionScript 3.0.
As per AS3 reference, alpha is from 0 to 1, not 0 to 100. That would suggest
`_root.sight.alpha = (100 - wall_collision)/100.0ยด
might work.
Can You try the following code. I have no prev exp with flash, but seems like You missed something.
The iterator xi should take values in range of distance, not only by one axis dx.
// calculate rotation based on target
_dx = this.x - _root.hero.x;
_dy = this.y - _root.hero.y;
// the iteration is by distance in original article mentioned so
// keep dist
//=================================
_dist = Math.sqrt(_dx*_dx+_dy*_dy);
// which way to rotate
_rotateTo = getDegrees(getRadians(_dx, _dy));
// keep rotation positive, between 0 and 360 degrees
if (_rotateTo > barrel.rotation + 90) _rotateTo -= 360;
if (_rotateTo < barrel.rotation - 90) _rotateTo += 360;
// ease rotation
_trueRotation = (_rotateTo - barrel.rotation) / _rotateSpeedMax;
// update rotation
barrel.rotation += _trueRotation;
wall_collision = 0;
// xi iterations are to a distance
//== =======
OuterLoop: for (var xi=1; xi<=_dist; xi++)
{
var point_x:Number = this.x + xi*Math.cos(_rotateTo);
var point_y:Number = this.y + xi*Math.sin(_rotateTo);
if(_root.wall.hitTestPoint(point_x, point_y, true))
{
trace("HIT");
wall_collision = 100;
break OuterLoop;
}
}
_root.sight.x = this.x;
_root.sight.y = this.y;
_root.sight.rotation += _trueRotation;
// EDITED AFTER OTHERS SOLVED
// was
//_root.sight.alpha = 100 - wall_collision;
// should be:
// Alpha changed from [0, 100] scale to [0, 1] scale.
_root.sight.alpha = (100 - wall_collision) * 0.01;
// END OF SOLUTION
There is only slight modification to Your original code, marked by preceding //=====
EDIT:
And the winner is transparency range. Still, I do recommend to iterate to a distance, not to _dx.
Here is my actionscript that edited from http://circlecube.com/2009/02/random-movement-brownian-revisited-for-as3/:
//number of balls
var numBalls:uint = 50;
var defaultBallSize:uint = 8;
//init
makeDots();
function makeDots():void {
//create desired number of balls
for (var ballNum:uint=0; ballNum<numBalls; ballNum++){
var c1:Number = randomColor();
var c2:Number = randomColor();
//create ball
var thisBall:MovieClip = new MovieClip();
thisBall.graphics.beginFill(c1);
//thisBall.graphics.lineStyle(defaultBallSize, 0);
thisBall.graphics.drawCircle(defaultBallSize, defaultBallSize, defaultBallSize);
thisBall.graphics.endFill();
addChild(thisBall);
//coordinates
thisBall.x = Math.random() * stage.stageWidth;
thisBall.y = Math.random() * stage.stageHeight;
//percieved depth
//thisBall.ballNum = ballNum;
// thisBall.depth = ballNum/numBalls;
//thisBall.scaleY = thisBall.scaleX = thisBall.alpha = ballNum/numBalls;
//velocity
thisBall.vx = 0;
thisBall.vy = 0;
thisBall.vz = 0;
//ball animation
thisBall.addEventListener(Event.ENTER_FRAME, animateBall);
}
}
var dampen:Number = 0.95;
var maxScale:Number = 1.3;
var minScale:Number = .3;
var maxAlpha:Number = 1.3;
var minAlpha:Number = .3;
function animateBall(e:Event):void{
var thisBall:Object = e.target;
//apply randomness to velocity
thisBall.vx += Math.random() * 0.2 - 0.1;
thisBall.vy += Math.random() * 0.2 - 0.1;
thisBall.vz += Math.random() * 0.002 - 0.001;
thisBall.x += thisBall.vx;
thisBall.y += thisBall.vy;
//thisBall.scaleX = thisBall.scaleY += thisBall.vz;
//thisBall.alpha += thisBall.vz;
thisBall.vx *= dampen;
thisBall.vy *= dampen;
thisBall.vz *= dampen;
if(thisBall.x > stage.stageWidth) {
thisBall.x = 0 - thisBall.width;
}
else if(thisBall.x < 0 - thisBall.width) {
thisBall.x = stage.stageWidth;
}
if(thisBall.y > stage.stageHeight) {
thisBall.y = 0 - thisBall.height;
}
else if(thisBall.y < 0 - thisBall.height) {
thisBall.y = stage.stageHeight;
}
if (thisBall.scaleX > maxScale){
thisBall.scaleX = thisBall.scaleY = maxScale;
}
else if (thisBall.scaleX < minScale){
thisBall.scaleX = thisBall.scaleY = minScale;
}
if (thisBall.alpha > maxAlpha){
thisBall.alpha = maxAlpha;
}
else if (thisBall.alpha < minAlpha){
thisBall.alpha = minAlpha;
}
}
function randomColor():Number{
return Math.floor(Math.random() * 16777215);
}
The colour of the balls are totally random and I was wondering if it was possible to make it so the colours would be random but from a pre-defined list of colours.
Thanks a lot.
You can easily accomplish that by using an array of colors:
var colors:Array = [0xFF0000, 0x00FF00, 0x0000FF];
function randomColor():uint
{
return colors[int(Math.random()*colors.length)];
}
The above code will select randomly from red, green and blue. You can add new colors to the randomization by simply adding their hex code to the array.
function randomColor():Number{
var my_clrs_arr:Array =
new Array(0xFFCC00,0xFF0000, 0xFFFF00, 0xFFFFFF, 0xCCCC00, 0xFACC00);
var random_num = Math.floor(Math.random() * my_clrs_arr.lemgth);
return my_clrs_arr[random_num];
}
I have placed one large ball, "centerBall", in the center of the stage. Then I added in a
bunch of smaller ones, giving them random sizes and velocities. These will move with basic motion code and bounce off the walls. On each frame, did a distance-based collision check between each moving ball and the center ball. If I got a collision, I've calculated an offset spring target based on the angle between the two balls and the minimum distance.
There is still one problem: some of the smaller balls bypass "centerBall" boundaries and then bounce off. You can see that in the attached image. Why is happening this?
Here is the code:
import flash.display.Sprite;
import flash.events.Event;
public class Bubbles extends Sprite
{
private var balls:Array;
private var numBalls:Number = 10;
private var centerBall:Ball;
private var bounce:Number = -1;
private var spring:Number = 0.2;
public function Bubbles()
{
init();
}
private function init():void
{
balls = new Array();
centerBall = new Ball(100, 0xcccccc);
addChild(centerBall);
centerBall.x = stage.stageWidth / 2;
centerBall.y = stage.stageHeight / 2;
for(var i:uint = 0; i < numBalls; i++)
{
var ball:Ball = new Ball(Math.random() * 40 + 5, Math.random() * 0xffffff);
ball.x = Math.random() * stage.stageWidth;
ball.y = Math.random() * stage.stageHeight;
ball.vx = Math.random() * 6 - 3;
ball.vy = Math.random() * 6 - 3;
addChild(ball);
balls.push(ball);
}
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void
{
for(var i:uint = 0; i < numBalls; i++)
{
var ball:Ball = balls[i];
move(ball);
var dx:Number = ball.x - centerBall.x;
var dy:Number = ball.y - centerBall.y;
var dist:Number = Math.sqrt(dx * dx + dy * dy);
var minDist:Number = ball.radius + centerBall.radius;
if(dist < minDist)
{
var angle:Number = Math.atan2(dy, dx);
var targetX:Number = centerBall.x + Math.cos(angle) * minDist;
var targetY:Number = centerBall.y + Math.sin(angle) * minDist;
ball.vx += (targetX - ball.x) * spring;
ball.vy += (targetY - ball.y) * spring;
}
}
}
private function move(ball:Ball):void
{
ball.x += ball.vx;
ball.y += ball.vy;
if(ball.x + ball.radius > stage.stageWidth)
{
ball.x = stage.stageWidth - ball.radius;
ball.vx *= bounce;
}
else if(ball.x - ball.radius < 0)
{
ball.x = ball.radius;
ball.vx *= bounce;
}
if(ball.y + ball.radius > stage.stageHeight)
{
ball.y = stage.stageHeight - ball.radius;
ball.vy *= bounce;
}
else if(ball.y - ball.radius < 0)
{
ball.y = ball.radius;
ball.vy *= bounce;
}
}
}
Click here to see the pic
The problem you have is that you are doing collision detection based on them frames and not the positions.
You need to check where it is now and where it was last frame so you can keep track of its movements. This is why it goes through your center ball because you check in the current frame for a collision.
Here is a link to a time based collision detection of circles.
Timed based collision
Hope this helps ; )
UPDATE
I now have this but only one ball bounces when the flash movie is run
var array:Array = new Array;
var dx:Number = Math.random() * 20;
var dy:Number = Math.random() * 10;
var velX:Number = 5;
var velY:Number = 5;
addEventListener(Event.ENTER_FRAME, movearray);
for (var i:Number = 0; i < 20; i++) {
var ball:Ball = new Ball();
ball.x = Math.random() * 550;
ball.y = Math.random() * 400;
addChild(ball);
array.push(ball);
}
function movearray(evt:Event):void {
ball.x += velX;
ball.y += velY;
if (ball.x > stage.stageWidth - ball.width / 2 || ball.x < 0 + ball.width /2){
velX *= -1;
}
else if (ball.y > stage.stageHeight - ball.height / 2 || ball.y < 0 + ball.height /2){
velY *= -1;
}
}
Thanks any help appreciated.
Of course only one ball moves, that's because in your movearray function you are only referring to one ball instance. Since you are storing are your ball instances in an array, you have to loop through that array to check for every ball. Also, since each ball moves independently, is mandatory to store an array of ball velocities as well.
For example:
import flash.geom.Point;
var numBalls:uint = 20;
var arrayBalls:Array = new Array();
var arrayVels:Array = new Array();
//I don't know what is this for
//var dx:Number = Math.random() * 20;
//var dy:Number = Math.random() * 10;
var initialVelX:Number = 5;
var initialVelY:Number = 5;
addEventListener(Event.ENTER_FRAME, movearray);
for (var i:uint = 0; i < numBalls; i++)
{
var ball:Ball = new Ball();
ball.x = Math.random() * 550;
ball.y = Math.random() * 400;
addChild(ball);
arrayBalls.push(ball);
//use a Point to store velocities in two axis
//you could also set random starting velocities
//so each ball would move differently initially
var vel:Point = new Point(initialVelX,initialVelY);
arrayVels.push(vel);
}
function movearray(evt:Event):void
{
var ball:Ball;
var vel:Point;
for (var i:uint = 0; i < numBalls; i++)
{
ball = arrayBalls[i];
vel = arrayVels[i];
ball.x += vel.x;
ball.y += vel.y;
if (ball.x > stage.stageWidth - ball.width / 2 || ball.x < 0 + ball.width /2)
{
vel.x *= -1;
}
else if (ball.y > stage.stageHeight - ball.height / 2 || ball.y < 0 + ball.height /2)
{
vel.y *= -1;
}
}
}
To move a ball you need to specify velocity for it. Suppose a ball has velocity vx, vy. And the time between successive call of movearray is dt. So the new position of the ball is:
dx = vx * dt;
dy = vy * dt;
xNew = xOld + dx;
yNew = nOld + dy;
When a ball hits top or bottom part of the screen you need to negate vy. When ball hits left or right end you need to negate vx. For example, vx is positive and thus the ball is moving right. When it hits the right end you need to make vx = -vx, so vx is now negative and the ball will start moving left.
So you have to store velocity (may be random, just like the initial positions) for every ball and inside movearray calculate dt and new positions. And if any ball hits the wall then negate the velocity accordingly. To test the hit on wall you need to compare their x, y coordinates with screen width, height etc. To calculate dt you can keep track of calling time of movearray.
dt = currentTime - lastTimeCalled;
lastTimeCalled = currentTime;
Note that, this method will only collide on screen boundary, it won't detect ball-ball collision.