I have this code which makes the ball bounce, but what I am looking for is to shoot bullets from the ground and once they hit the ball they should bounce it back upwards. The goal is not to let the ball hit the ground. I am sure this has been done before, but I guess am too dumb to figure it out.
The Code:
package {
public class ball extends MovieClip {
var timer:Number=0;
var initialPos:Number=0;
var finalPos:Number=0;
var currentPos:Number=0;
var initialSpeed:Number=0;
function ball() {
startFallingBall();
}
function moveBallDown(e:Event) {
timer+=1;
this.y = initialPos + .5 *(timer * timer);
checkBottomBoundary();
}
function moveBallUp(e:Event) {
timer+=1;
var posA=this.y;
this.y = currentPos - initialSpeed*timer + .5*(timer * timer);
var posB=this.y;
checkTopBoundary(posA, posB);
}
function checkBottomBoundary() {
if (this.y+this.height>stage.stageHeight) {
finalPos=this.y;
stopFallingBall();
}
}
function checkTopBoundary(firstPos:Number, secondPos:Number) {
if (secondPos>firstPos) {
stopRisingBall();
startFallingBall();
}
}
function startFallingBall() {
timer=0;
initialPos=this.y;
this.addEventListener(Event.ENTER_FRAME, moveBallDown);
}
function stopFallingBall() {
this.removeEventListener(Event.ENTER_FRAME, moveBallDown);
if (finalPos-initialPos<.1*this.height) {
stopRisingBall();
} else {
startRisingBall();
}
}
function startRisingBall() {
initialSpeed=Math.sqrt(Math.abs(finalPos-initialPos));
timer=0;
currentPos=this.y;
this.addEventListener(Event.ENTER_FRAME, moveBallUp);
}
function stopRisingBall() {
this.removeEventListener(Event.ENTER_FRAME, moveBallUp);
}
function stopEverything() {
this.removeEventListener(Event.ENTER_FRAME, moveBallUp);
this.removeEventListener(Event.ENTER_FRAME, moveBallDown);
}
}
}
A simple way is to use DisplayObject's hitTestObject. This checks for overlapping. You can of course write something more advanced yourself.
For each game update you check if any bullets hit the ball, and take action if they did. You should also consider separating game logic and display updating. Look into using Timer.
package {
public class BallGame extends Sprite
{
private var ball:Ball;
private var bullets:Array;
public function BallGame() {
addEventListener(Event.ENTER_FRAME, checkCollision);
addEventListener(MouseEvent.CLICK, fireBullet);
}
private function fireBullet(e:MouseEvent):void {
// adds a fired bullet to an array so we can loop over all bullets
bullets.push(new Bullet());
}
private function checkCollision(e:Event):void {
// loops through all bullets in play
for each(var bullet:Bullet in bullets) {
// check if the bullet is inside the ball
if (ball.hitTestObject(bullet)) {
// the bullet hit the ball
ball.startRisingBall();
// TODO: remove the bullet :)
}
}
}
}
}
There's a function for preforming hittest you can read about it here:
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#hitTestObject()
Though with circles it's pretty easy if you know the radius, if the distance between the centers is less than the sum of their radii; they touch each other.
But by looking at your code you should properly rewrite the whole thing. The ball really should not handle collision and movement logic internally. It should only have a positon and speed vector, and the movement and colission logic should be in other classes.
As for the code for reaction, it depends on how complicated you want it. And can be anthing from simply flipping the y part of the speed vector, to fairly complicated math.
There's alot of toturial and examples in this area, I think you would be better of finding some on google and playing with it. Then write your own.
Related
I have a question about action script 3.
I am making a game, the basic rule of the game is:
an object falls from the top
the hero(user) has to avoid the object
if the object hits the ground or the hero: the hero dies or the object falls again from the top.
I am using the add child method for the object, and timer function for the fall.
the problem is:
when the object hits the ground, the function does not loop. it ends just like that. so there wont be any falling objects anymore.
please help me. thanks :)
stage.addEventListener(Event.ENTER_FRAME, addfire1);
function addfire1(e:Event):void
{
if (api1==false)//if object is not on the stage
{
randomx = randomRange();//generate random X
addChild(api);
api.x = randomx;//set x
api1 = true;//object is now on stage
}
if (api.hitTestObject(hero) || api.hitTestObject(ground))
{
falltimer.stop();
//stop timer;
falltimer.reset();
//reset fall Count to zero ;
removeChild(api);//object removed
api1=false;
}
}
function firefall(Event:TimerEvent):void
{
if (api1)
{
api.y += 20;//set speed y
}
}
Just separate the two cases: hero and floor.
if (api.hitTestObject(hero))
{
falltimer.stop();
//stop timer;
falltimer.reset();
//reset fall Count to zero ;
removeChild(api);//object removed
api1=false;
}
else if (api.hitTestObject(ground))
{
//reset object's position
api.y = -20;
}
Assuming there is only one object falling at the time I'd not remove the object but instead just move it to the original y position and a new x position. And instead of having the timer i'd create an update-function that runs every time you enter a frame.
stage.addEventListener(Event.ENTER_FRAME, update);
private function update(e:Event):void
{
if(!api1) //if object is not on the stage (same as api1==false)
{
addfire1();
}
if(api1) //if object if on stage (same as api1==true)
{
firefall();
}
}
private function addfire1(e:Event):void
{
randomx = randomRange();//generate random X
addChild(api);
api.x = randomx;//set x
api1 = true;//object is now on stage
if (api.hitTestObject(hero))
{
hitHero(); //execute what should happen to the hero when the he is hit
resetFire();
}
else if(api.hitTestObject(ground))
{
resetFire();
}
}
private function firefall(Event:TimerEvent):void
{
api.y += 20;//set speed y
}
private function resetFire():void
{
api.x = randomRange();
api.y = 0;
}
If you write it like this you don't have to mix stuff up. Try to keep everything seperated, one function does one thing. If you are making a big game try to seperate everything in classes.
Hopefully this fixes the problem and makes it easier for you to finish the game :)
So I have been making this game with Action Script 3, and CS5.5. What you are trying to do is avoid asteroids while you fly through space. I thought it would be cool to have the planets in out solar system be moving down the screen throughout the game in the background. Kinda to make it look like you were flying pass them. The way I did this was I added five to their y coordinate each frame per second. Once their y coordinate reached 600 ( the bottom of the screen ) I would add a new planet which would do the same thing. For some reason once I got to Saturn everything got weird. Saturn came to early, and so did Uranus. I had no idea what was going on. I have been frustrated with this for a good hour. Here is the part where I think there is the problem.
public function onTick(timerEvent:TimerEvent):void
{
earth.PlanetMovement(5);
if (earth.y==600)
{
mars.PlanetsStart(300, -100);
addChild( mars );
levels=levels+5;
}
mars.PlanetMovement(5);
if (mars.y==600)
{
jupiter.PlanetsStart(300,-150);
addChild (jupiter);
levels=levels+10;
}
jupiter.PlanetMovement(5);
if (jupiter.y==600)
{
saturn.PlanetsStart(300,-155);
addChild (saturn);
levels=levels+20;
}
saturn.PlanetMovement(5);
if (saturn.y==600)
{
uranus.PlanetsStart(300,-160)
addChild ( uranus);
levels=levels+25;
}
uranus.PlanetMovement(5);
PlanetMovement, and PlanetsStart are two functions in the Planets class. If you need more info please tell me.
EDIT: I guess I should explain further. PlanetsStart is a function that has the starting coordinate of each movieclip. So once earth reached a y coordinate of 600, then mars starts at (300, -100). Then it is added to the screen. levels is a variable that raises the score each fps. PlanetMovement is how much each movieclip will move each fps. If I were to use >= then the score would raise too much.
This is exactly what happens. earth shows up where it is supposed to. Then mars showd up on time. Then for some reason Saturn pops up in the middle of mars, and Jupiter. After this Saturn reaches the bottom making Uranus appear. Then Jupiter reaches the bottom, and everything works as it should. Saturn shows up, and then Uranus in order
Ugh. Unfortunately, this is more complex than you are implying. It would really help you to write a code plan of some sort first, develop some psuedo-code that will show basic logic (and the logic errors), and then code after that.
Below is an example of a better way to structure this idea. However, I don't think your idea is terrible memory conscious. You should only bring in the planets as needed, and remove them as needed as well. Look into a technique called "object pooling" to help better structure this.
Solar system class:
package
{
import flash.display.MovieClip;
import flash.events.Event;
import flash.geom.Point;
public class SolarSystem extends MovieClip
{
private var PLANET_NAMES:Array = new Array("earth", "mars", "jupiter", "saturn", "uranus");
// you will have to fix these values:
private var PLANET_STARTS:Array = new Array(new Point(300, -100), new Point(300, -100),new Point(300, -100),new Point(300, -100),new Point(300, -100));
private var planets:Array;
private var maxY:Number = 600;
private var speed:Number = 5;
private var levels:Number = 0;
private var levelIncrement:Number = 5;
public function SolarSystem()
{
super();
addEventListener(Event.ADDED_TO_STAGE, initHnd, false, 0, true);
}
private function initHnd(e:Event):void
{
removeEventListener(Event.ADDED_TO_STAGE, initHnd);
runPlanets();
addEventListener(Event.ENTER_FRAME, frameHnd, false, 0, true);
}
private function runPlanets():void
{
for (var i:int = 0; i < PLANET_NAMES.length; i++)
{
planets[i] = new Planet();
planets[i].name = PLANET_NAMES[i];
Planet(planets[i]).startPlanet(PLANET_STARTS[i]);
this.addChild(planets[i]);
}
}
private function frameHnd(e:Event):void
{
if(planets && planets.length > 0){
// move all the planets until they are too low, then remove them.
// decrementing loop because planets will be being removed occasionally.
for (var i:int = planets.length -1; i >= 0; i--)
{
if (planets[i] && Planet(planets[i]).y >= maxY) {
// this seems very strange to me, but it will replicate what your code says:
levels += (levels + levelIncrement);
try {
removeChild(Planet(planets[i]));
planets[i] = null;
}catch (e:Error) { }
} else if ( Planet(planets[i]).isMoving ){
Planet(planets[i]).movePlanet(0, speed);
}
}
} else {
removeEventListener(Event.ENTER_FRAME, frameHnd);
}
}
}
}
and here is the planet class:
package
{
import flash.display.MovieClip;
public class Planet extends MovieClip
{
private var _isMoving:Boolean = false;
public function Planet()
{
super();
}
public function startPlanet(sx:Number, sy:Object):void
{
this.x = sx;
this.y = sy;
isMoving = true;
}
public function movePlanet(dx:Number, dy:Number):void
{
if (isMoving)
{
this.x += dx;
this.y += dy;
}
}
public function get isMoving():Boolean
{
return _isMoving;
}
public function set isMoving(value:Boolean):void
{
_isMoving = value;
}
}
}
Again, this is not the best way to do this, but it is more manageable to group concepts and actions into classes.
HTH.
Right off the bat I can see a major issue. Change all the statements from:
if (saturn.y==600)
to
if (saturn.y>=600)
Just saying == means that you're only going to trigger the conditional in the case that your Y value is exactly 600.
Note you can also change stuff like levels = levels+20 to simply levels += 20;.
When moving stuff around like this it is a good idea to check the position by using ">=" instead of "==". What if your object starts at y=1? Then it will never reach 600 it will just keep on going down forever.
Also you seem to be calling PlanentMovement even when PlanetsStart hasn't been called yet.
I'm quite new to AS3, and I need some help. I'm trying to make a game like Mario. I've made a character which can jump right now, but I've got some problems with collision detection.
I would like my character to jump on a bar, which is placed higher. My collision detection doesn't work at all I gues..
I've made a cirle which has a instance name mcMain and I've made a MovieClip of it. T also made a rectangle which has a instance name balkje, I also made aMovieClip of it.
I hope you can tell me what is wrong about my code and what I've to change to make the collision detection work! Thanks a lot!
balkje.addEventListener(KeyboardEvent.KEY_DOWN, drag);
stage.addEventListener(KeyboardEvent.KEY_UP, drop);
function drag(e:KeyboardEvent):void
{
e.target.startDrag();
}
function drop(e:KeyboardEvent):void
{
stopDrag();
if (balkje.hitTestObject(mcMain))
{
trace("Collision detected!");
}
else
{
trace("No collision.");
}
}
I think you should be using mouseEvent, not keyboard event.
How can you drag with the keyboard?
balkje.addEventListener(MouseEvent.MOUSE_DOWN, drag);
balkje.addEventListener(MouseEvent.MOUSE_UP, drop);
function drag(e:MouseEvent):void
{
e.target.startDrag();
}
function drop(e:MouseEvent):void
{
e.target.stopDrag();
if (balkje.hitTestObject(mcMain))
{
trace("Collision detected!");
}
else
{
trace("No collision.");
}
}
I need to develop my own drag function in AS3 (instead of using startDrag) because I'm resizing a MovieClip.
I'm doing this:
public class resizeBR extends MovieClip {
var initialScaleX, initialScaleY;
public function resizeBR() {
this.addEventListener(MouseEvent.MOUSE_DOWN, initResize);
this.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = e.target.scaleX;
initialScaleY = e.target.scaleY;
e.target.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
e.target.x += e.localX;
e.target.y += e.localY;
e.target.parent.parent.width += mouseX;
e.target.parent.parent.height += mouseY;
// Keep its own scale
e.target.scaleX = initialScaleX;
e.target.scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
e.target.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}
But the drag feature is not working fluently. I mean, when I drag a MovieClip from class resizeBR I need to move slowly my mouse cursor or it's not going to work propertly.
resizeBR is a MovieClip as a child of another MovieClip; the second one is which I have to resize.
What am I doing wrong?
Thanks!
Thanks all for your answers, but I found a great classes to do what I want.
http://www.senocular.com/index.php?id=1.372
http://www.quietless.com/kitchen/transform-tool-drag-scale-and-rotate-at-runtime/
I'm not really sure if I completely understand what you mean. But I think your problem lies with your MOUSE_MOVE handler.
In your current example you're resizing your target only when moving your mouse over the target. When you're moving your mouse fast enough it's possible your mouse leaves the target, casuing it to stop resizing. When I'm writing my own drag handlers I usually set the MOUSE_MOVE and MOUSE_UP listeners to the stage.
Your class would end up looking something like this:
public class resizeBR extends MovieClip
{
var initialScaleX, initialScaleY;
public function resizeBR()
{
addEventListener(MouseEvent.MOUSE_DOWN, initResize);
addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
public function initResize(e:MouseEvent):void
{
initialScaleX = scaleX;
initialScaleY = scaleY;
stage.addEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
public function startResize(e:MouseEvent):void
{
x += e.localX;
y += e.localY;
parent.parent.width += mouseX;
parent.parent.height += mouseY;
// Keep its own scale
scaleX = initialScaleX;
scaleY = initialScaleY;
}
public function stopResize(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_MOVE, startResize);
}
}
There are a couple reasons the resizing is jumpy. First, like rvmook points out, you'll need to make sure you support the mouse rolling off of the clip while its being resized. Since there is not an onReleaseOutside type of event in AS3, you have to set listeners to the stage, or some other parent clip. If you have access to the stage, that is best. If not, you can use the root property of your resizable clip, which will reference the highest level display object you have security access to. Setting mouse events to the root is a little wonky, because for them to fire, the mouse needs to be on one of the root's child assets - whereas the stage can fire mouse events when the mouse is over nothing but the stage itself.
Another reason you might be seeing some strange resizing behavior is because of using the localX/Y properties. These values reflect the mouseX/mouseY coordinates to the object being rolled over - which might not necessarily be your clip's direct parent.
I tend to avoid having classes access their parent chain. You might want to consider placing the resizing logic in the clip you want resized, and not in one of its children. Here is simple self resizing example:
package {
import flash.display.MovieClip;
import flash.events.MouseEvent;
import flash.events.Event;
public class ResizableBox extends MovieClip {
public function ResizableBox() {
addEventListener(MouseEvent.MOUSE_DOWN, startResize);
}
private function startResize(evt:MouseEvent):void {
stage.addEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.addEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function stopResize(evt:MouseEvent):void {
stage.removeEventListener(MouseEvent.MOUSE_MOVE, handleResize);
stage.removeEventListener(MouseEvent.MOUSE_UP, stopResize);
}
private function handleResize(evt:MouseEvent):void {
this.scaleX = this.scaleY = 1;
this.width = this.mouseX;
this.height = this.mouseY;
}
}
}
ResizableBox is set as the base class of a MC in the library.
I'm trying to have a rotatable line, controlled by the arrow keys. When you click the mouse, a ball drops from the cursor, and stops when it hits the line.
However, the balls always stop at the highest point of the line, across a line parallel to the x-axis.
My document class is as follows:
package
{
import flash.display.MovieClip;
import flash.events.*
import flash.display.Stage
import ball
public class Engine extends MovieClip
{
public static var stageRef:Stage
private static var leftKey:Boolean = false
private static var rightKey:Boolean = false
public static var pi = Math.PI
public static var lineRotate:Number = 0
public static var spinRate:Number = 60
public static var ground:line = new line()
public function Engine()
{
// constructor code
stage.addEventListener(Event.ENTER_FRAME, loop)
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler)
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler)
stage.addEventListener(MouseEvent.CLICK, addBall)
stageRef = stage
ground.x = 300
ground.y = 200
stage.addChild(ground)
}
private static function keyDownHandler(e:KeyboardEvent)
{
if (e.keyCode == 37) //left
{
leftKey = true
}
if (e.keyCode == 39)
{
rightKey = true
}
}
private static function keyUpHandler(e:KeyboardEvent)
{
if (e.keyCode == 37) //left
{
leftKey = false
}
if (e.keyCode == 39) //right
{
rightKey = false
}
}
public function loop(e:Event)
{
spin()
}
public static function addBall(e:MouseEvent) //adds ball
{
var tempBall:ball = new ball()
tempBall.x = e.stageX
tempBall.y = e.stageY
stageRef.addChild(tempBall)
}
private static function spin() //spins the "ground" line
{
if (leftKey) // minus
{
lineRotate -= spinRate
}
if (rightKey) // plus
{
lineRotate += spinRate
}
ground.rotation = lineRotate * (pi / 180) //convert to radians
}
}
}
The class for the ball is as follows:
package
{
import flash.display.MovieClip;
import flash.events.*
public class ball extends MovieClip
{
public var vX:Number = 0
public var vY:Number = 2
private var gravity:Number = 0
public function ball()
{
// constructor code
addEventListener(Event.ENTER_FRAME, loop)
}
public function loop(e:Event)
{
this.x += vX
this.y += vY
this.vY += gravity
if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
{
removeSelf()
}
if (Engine.ground.hitTestObject(this))
{
trace('yep')
stopBall()
}
else
{
trace('nope')
}
}
public function removeSelf()
{
removeEventListener(Event.ENTER_FRAME, loop)
this.parent.removeChild(this)
}
public function stopBall()
{
gravity = 0
vY = 0
vX = 0
}
}
}
I've uploaded my .swf to here.
The easiest thing you can do, for balls is a hitTestPoint with the third argument as true, that turns on shape detection.
ground.hitTestPoint(x,y,true);
http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#hitTestPoint()
Ok, so, hm, you can check a single point on the ball, like it's bottom point, or you can check an array of points along the bottom of the ball for easier precision... this is the quickest way to do this if you're not planing anything more complex then this. If, however, you want to create a full fleshed game, leave it to a 2d physics library like http://www.box2dflash.org/.
Forget skinners collision detection for a bigger game(something small like this could survive with it though), as it is bitmap based and would kill performance much more then box2D, it's a nice mask example, but it not a good idea to use for performance reasons.
I've changed your code a little bit. I've hittested a bunch of points on the bottom part of the ball, bear in mind that the ball's 0,0 inside the movieclip is centered on the balls center.
package
{
import flash.display.MovieClip;
import flash.events.*;
import flash.geom.Point;
public class ball extends MovieClip
{
public var vX:Number = 0;
public var vY:Number = 2;
private var gravity:Number = 0;
public function ball()
{
// constructor code
addEventListener(Event.ENTER_FRAME, loop);
}
public function loop(e:Event)
{
this.x += vX;
this.y += vY;
this.vY += gravity;
if (this.x > stage.stageWidth || this.x < 0 || this.y < 0 || this.y > stage.stageHeight)
{
removeSelf();
}
/*we will now check something like 18 points on the bottom side of the ball for colision
instead of just the bottom, you can probably guess why... if you cant, replace i on line
43 (var angleInRadians...) with 270 to test and then drop balls on a slopped ground surface... of course
you should definitely juse a physics engine like http://www.box2dflash.org/ for anything more complex.
*/
for (var i:int = 180; i<=360; i+=10)
{
/*keep in mind that ball.rotation property has 0 at the top of the ball, while here for these we are using the standard
Cartesian coordinate system. The effect of this on rotation would be that it is +90 and for X(yes, X) it would be Math.SIN(),
not Math.COS()!!!, and for Y it would be Math.sin() with a minus in front because of the flash coordinate system y axis rotation.
It's not really related to this, but it's a point that has anoyed me to no end in trig related stuff in flash when I was starting.
*/
var angleInRadians:Number = i / 180 * Math.PI;
var posX:Number = this.x + Math.round(Math.cos(angleInRadians) * width / 2);
var posY:Number = this.y - Math.round(Math.sin(angleInRadians) * height / 2);//minus because y in flash is upside down
Engine.ground.hitTestPoint(posX,posY, true);
if (Engine.ground.hitTestPoint(posX,posY, true))
{
stopBall();
}
else
{
}
}
/*if (Engine.ground.hitTestObject(this))
{
trace('yep')
stopBall()
}
else
{
trace('nope')
}*/
}
public function removeSelf()
{
removeEventListener(Event.ENTER_FRAME, loop);
this.parent.removeChild(this);
}
public function stopBall()
{
gravity = 0;
vY = 0;
vX = 0;
}
}
}
Unrelated to this above, you need to rethink your OOP a little bit, you are doing things a little bit wrong :). A project this small would cope, but anything bigger would give you headaches. Don't take this as an attack, I want to try to guide you to the "right" path and show you the logical fallacies in your code, I am not mentioning this to "pwn" you cause your oop is bad :).
For example, gravity, inside class ball? Yikes... what if you wanted to have gravity for ninjas, shurikens, ragdolls, etc? Are you going to subclass Ninjas out of the ball class?
Ninja extends ball? That would be fun.
Do you think that there might be a better place for that gravity thing? Is "gravity" a property of the ball at all(err, tehnically it is, but not this homogeneous gravity you are putting here, it's more like a permeating, all present thing, cause there is some huge body that we are too close to)? It should be in the Engine...
Also, you did Engine.ground thing... now, this is a static variable... this is another bad thing, very bad thing :)
The reason is similar to the previous example, but a little bit turned around. What if you want to reuse ball inside, say Engine2? Or Crytek engine? or UDK? I think it might be sligtely problematic, don't you think? You would have to go in and change the ball class... Imagine if every code you used forced you to do that...
Basically, you probably could have done something like this:
var bla:ball = new ball(ground);
this is better, muuuch better... now we can use it in crytek, udk and Engine2 easily...
Technically, you want to avoid things like static variables of this kind in your code, the idea is called encapsulation. The idea is that you hide the internal workings of classes from other classes that are unrelated to it or should not know about it. The less you NEED to know, the more portable, reusable (yada yada) your classes are. Engine.ground is a huge encapsulation breaker because ball has absolutely zero need to know about class Engine, it should only be concerned with a reference to the ground itself...
In time you will learn all of this, patterns in programming, and particularly , which patterns save time, and which are appalling, and why (static variables have their use, but only for ultra simple stuff, avoid Singletons as well if you know what they are).
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)
Sorry for the headache I've caused you...
Remember, use a physics engine for more complex code, don't reinvent the wheel(reuse everything for your projects that is good for them/sufficient for them)... It's fun to test stuff like this, but other folks have done it better before you, and unless you specifically need to delve into their domain, you don't need to concern yourself with every little detail. You are probably trying to build games/something, not bother with Newtonian dynamics...
HitTest is just fine for simple stuff though, but this is bordering on simple... :)we are already slightly into physics here, and you might find you need a more robust solution soon...
So generally, try to make your code "robust" by thinking about encapsulation and dependencies between your classes. If a dependency feels unatural (ie ball depends on Engine) then something is probably wrong, and will break later, will break for another game or engine or... I'll end it here...
Take care and have fun.
According to the article Collision detection methods: hit-test and hit-testobject alternatives, that is by design:
As you can see Flash uses the bounding
boxes of objects - it takes the
highest and lowest possible points,
the leftmost and rightmost points and
draws a rectangle around them.
The author mentions several alternatives to workaround this limitation, such as using hitTestPoint or Grant Skinner's Shape based collision detection.