Resetting score in AS3 - actionscript-3

I am creating a "dice spinner" game by using Flash action script 3, The rules are if the player rolls a 7 or 11 on first throw then they win but if the user rolls a 2,3 or 12 on the first throw then they lose. If the first throw is 4,5,6,8,9 or 10 then that sum becomes the point and that user has to make a point by rolling again until they make the point (they win) or they throw a 7 (they lose)
I have succesfully created the functions for it to spin, generate a random between 1-6 on each "dice" and to add the points up from total but my problem is if the user wins or lose. I can't figure out on how to reset the points back to zero when the user rerolls from last attempt
The code is blow.
import flash.events.MouseEvent;
spinner_mc.stop();
spinner2_mc.stop();
roll_btn.addEventListener(MouseEvent.CLICK, startspin);
stop_btn.addEventListener(MouseEvent.CLICK, stopspin);
spinner_mc.addEventListener(Event.ENTER_FRAME, spin);
spinner2_mc.addEventListener(Event.ENTER_FRAME, spin)
var spinning = false
function spin(evt){
if(spinning){
spinner_mc.rotation += 5
spinner2_mc.rotation += 5
}
} //end spin function
function startspin(mEvt:MouseEvent){
spinning = true
} //end startspin function
var points:int = 0;
function stopspin (mEvt){
if (spinning){
spinning = false
var nextVal = newVal()
var nextVal2 = newVal2()
var addPoints = nextVal + nextVal2
points += nextVal + nextVal2
spinner_mc.gotoAndStop(nextVal)
spinner_mc.rotation = 0
spinner2_mc.gotoAndStop(nextVal2)
spinner2_mc.rotation = 0
value_txt.text = "point = " + points + ", new value " + addPoints
}
if ((points == 0) && (addPoints == 7) || (addPoints == 11))
{
info_txt.text = "You win!! You rolled a 7 or 11 on first throw!"
}
if ((points == 0) && (addPoints == 2) || (addPoints == 3) || (addPoints == 12))
{
info_txt.text ="You lose! You rolled a 2, 3 or 12 on first throw!"
}
} // end stopspin function
function newVal(){
var temp:int;
temp = Math.random() * 6 + 1;
return temp;
}
function newVal2(){
var temp:int
temp = Math.random() * 6 + 1;
return temp;
}

You need to check for points and addPoints before you add points to the player, while your code has check for points outside the if (spinning) block. This makes your checks go awry even if points is yet zero. So, you first get the spin values, then check, then assign values.
function stopspin(evt:MouseEvent):void {
if (!spinning) return; // this is shorter than wrapping the entire function
// body into a single "if (spinning)" block. If we don't spin, get out
spinning=false;
var nextVal1:int=newVal();
var nextVal2:int=newVal(); // no need of two functions doing the same :)
var addPoints:int=nextVal1+nextVal2;
spinner_mc.gotoAndStop(nextVal1);
spinner_mc.rotation = 0;
spinner2_mc.gotoAndStop(nextVal2);
spinner2_mc.rotation = 0; // give values to spinners
// now check phase
if (points==0) {
// this means first throw.
if ((addPoints==7) || (addPoints==11)) {
// player wins, insert win code
} else if((addPoints==2)||(addPoints==3)||(addPoints==12)) {
// player loses, insert lose code
} else {
points=addPoints; // otherwise making the player do second throw
// add other code, say inform the player that he has to throw again
}
} else {
// not a first throw. We have the "point" in "points" variable right now
if (addPoints==points) {
// player wins with second throw, add win code
points=0; // okay, back to first throw
} else if (addPoints==7) {
// player loses second throw, add relevant code
points=0; // again, back to first throw
} else {
// else no match, oh well.
}
}
}
Also, you should know better how to use event listeners if you want several objects to listen with a single function (in your case, spin). See, right now you have your spin function to update both spinners, and you have it being called twice per frame, so your spinners are spinning at twice the desired speed. You can get the actively listening movie clip using the Event class object passed into the listener, you need to get its target property. So, your listener can be rewritten like this:
function spin(evt:Event):void {
var mc:DisplayObject=evt.target; // the object that's listening
mc.rotation+=5; // now turn it.
}
And last, please place semicolons at the end of each line. Even if AS3 compiler does not strictly require this, it can save you from some really weird mistakes.

Related

How can I capture if the user clicked a wrong button?

I made a game remember for the first fishes. I found a code like this. I also have wrong buttons.
Wrong buttons list:
pinkButton.addEventListener(MouseEvent.CLICK, pinkClick);
whiteButton.addEventListener(MouseEvent.CLICK, whiteClick);
greyButton.addEventListener(MouseEvent.CLICK, greyClick);
And I use this code
import flash.events.MouseEvent;
var checkString:String = "";
 
//Create event listeners and their functions.
YellowButton.addEventListener(MouseEvent.CLICK, yellowClick);
RedButton.addEventListener(MouseEvent.CLICK, redClick);
BlueButton.addEventListener(MouseEvent.CLICK, blueClick);
/*True choices*/
function yellowClick(evt:Event):void
{
//In each event listener function, add a letter or
//string to the checkString variable.
checkString += "y";
//Then, see if the string matches or not.
check();
}
function redClick(evt:Event):void
{
checkString += "r";
check();
}
function blueClick(evt:Event):void
{
checkString += "b";
check();
}
/*True choices*/
 
//If the proper sequence is red, yellow, blue, the string would read "ryb".
function check():void
{
if(checkString == "ryb")
{
//Clear the checkString for convenience before going on.
clearString();
//CODE TO GO TO NEW FRAME
gotoAndStop(3);
}
else
{
//Make sure the string is at least 3 characters long.
if(checkString.length >= 3)
{
clearString();
gotoAndStop(1);
}
}
}
function clearString():void
{
//You will want to have a function for clearing the string.
//This is especially useful if you have a button for "start over."
checkString = "";
}
 
if I click yellow, red, blue it works. How can I make wrong choices? Do I have to write a code for all possibilities? Player has 1 chance. For example if player clicked 2 false and 1 true button,or 2 true and 1 false, this results in a loss for the player.
Use an array of values. Like
var correctSequence:Array = ["r", "y", "b"];
then have a incrementing variable to give you control over traversing the array
var arrayPosition:int = 0;
and you need a variable to hold the Boolean value of whether or not there have been any wrong guesses:
var noneWrong:Boolean = true;
Then you could do something like
private function playerNextGuess(e:MouseEvent):void{
if (e.target._color == correctSequence[arrayPosition] && noneWrong == true){
arrayPosition++;
if (arrayPosition == 3){
playerWin();
arrayPosition = 0;
}
} else {
// put whatever logic you want for when the guess is wrong
noneWrong = false;
arrayPosition++;
if (arrayPosition == 3){
playerLose();
arrayPosition = 0;
}
}
This will make it so that 3 guesses are made before the results (right or wrong) are given to the player. But it won't tell the player which were right and which were wrong. If none are wrong, the win function is called. If any are wrong the lose function is called. Is that what you wanted?
Hopefully that gets you going in the right direction. Let me know if anything I wrote isn't abundantly clear.

Resetting timer in AS3

I'm frustrated. I have a button that when it is clicked on, three timers start. It is no problem the first time the button is clicked on because it runs through the three things it is suppose to do. The problem happens when I click on another button to move away from that area of the stage and then click back on the same button again. If I click back too quickly and hadn't finished letting the timer run out, then I see the tail-end of my sequence... it is not resetting.
You'd think this would be easy... that I should be able to just put in a timer.reset() but it doesn't appear to be working. I can't put the reset call into the code because it keeps saying that the timer I am referencing doesn't exist (TypeError: Error #1009: Cannot access a property or method of a null object reference.)
I tried different ways to make sure the timer was called before it tried to reset it but I keep getting the #1009 error and the various ways I have tried using the reset appear to be not working. I don't know both why timer.reset() won't work and how to get around this error. Thanks,
else if (
event.target.name == "num6a"
)
{
trace("num6a clicked on");
TweenLite.to(shape, 1, {x:-608.55, y:-423.05});
close2.visible = true;
close2.addEventListener(MouseEvent.CLICK, checkforclose);
shape.grey6.visible=true;
counterSix++;
if(counterSix > 1){
trace("inside counterSix");
myDelay_sec6a.reset()
myDelay_sec6b.reset()
myDelay_sec6c.reset()
//if(myDelay_sec6a != null){
//trace("myDelay_sec6a is not null!");
//myDelay_sec6a.stop();
//myDelay_sec6a.reset();
//}
}
//section6 timing
var myDelay_sec6a:Timer = new Timer(1000,1);
myDelay_sec6a.addEventListener(TimerEvent.TIMER, goTosec6a);
//myDelay_sec6a.reset();
myDelay_sec6a.start();
function goTosec6a(event:TimerEvent):void
{
TweenLite.to(shape.section6_a, 2, {alpha:1});
trace("seconds 1: cutting edge title appears");
}
var myDelay_sec6b:Timer = new Timer(3000,1);
myDelay_sec6b.addEventListener(TimerEvent.TIMER, goTosec6b);
//myDelay_sec6b.reset();
myDelay_sec6b.start();
function goTosec6b(event:TimerEvent):void
{
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
Taking a peak at your code the way it is currently setup you are going to have quite some trouble down the road making any adjustments to it. I'm not sure if you are doing this in the timeline or in a separate .as file, but you could have a fundamental scoping issue. That is your timer variables are being created in a function which means they are local to that function, so next time you click this button the variable myDelay_whatever is going to be null referenced ( which means running any of its methods is out of the question ). Either way if this is or is not the case, I rewrote some of this that makes it a bit more flexible.
Some of the things I changed include:
Moved your timer functions to remove local scoping ( currently they were setup inside a function perhaps? Either way it's better to have them separate if they are getting used a lot ).
Setup a way to track your timers and remove them when the function is called again.
Added evaluation of current timers that are running before you attempt to add a new one.
Also just as a side note, I haven't tested a single bit of this code but hopefully this puts you on the right track.
close2.visible = true;
close2.addEventListener(MouseEvent.CLICK, checkforclose);
shape.grey6.visible=true;
counterSix++;
if(counterSix > 1){
trace("inside counterSix");
var i:int;
for ( ; i < _timerList.length; i++ ) {
//reset all the timers
_timerList[i].timer.reset();
}
//clear the list
_timerList.length = 0;
}
addTimer( "timer_sec6a", 1000 );
addTimer( "timer_sec6b", 3000 );
//addTimer( "timer_sec6c", 1000 );
Then these are the functions I added:
private function goTosec6a(event:TimerEvent):void {
TweenLite.to(shape.section6_a, 2, {alpha:1});
trace("seconds 1: cutting edge title appears");
}
private function goTosec6b(event:TimerEvent):void {
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
private function goTosec6b(event:TimerEvent):void {
TweenLite.to(shape.section6_b, 2, {alpha:1});
TweenLite.to(shape.section6_c, 2, {alpha:1});
trace("seconds 3: first paragraph appears");
}
//create this somewhere to kepe track of your timers, dont forget to instantiate it _timerList = new Vector.<Object>();`
private var _timerList:Vector.<Object>;
//set the name parameter to timer_sec6a, timer_sec6b etc and it will setup the timer event function for you
private function addTimer( name:String, delay:Number, repeat:int=1 ):void {
//check to see if timer already exists before creating another
if ( !evalCurrentTimers( name ) ) {
//create new timer
var timer:Timer = new Timer( delay, repeat );
timer.addEventListener( TimerEvent.TIMER, this["goTo" + name.split("_")[1]] );
_timerList.push( {timer: timer, name: name} );
}
}
private function evalCurrentTimers( timerName:String ):Boolean {
var i:int;
//check to see that we don't already have a timer with the same name
for ( ; i < _timerList.length; i++ ) {
if ( _timerList[i].name == timerName ) {
return true;
}
}
return false;
}

Prevent Movie Clip Overlapsing

I'm creating a screen saver which requires a movie clip to be loaded to the stage at a random position based on the size of the screen, then fade out (which I have all of the animation within a movie clip as tweens)
I've hit a road block and can't figure out how to prevent the movie clips from overlapsing on top of each other. If anything, I'd like for them to appear at another random spot that does not cause the overlapse.
here is all of the code for my project:
stop();
import flash.display.MovieClip;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.Event;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
var greystoneLogos:Array = new Array ;
var countTimeArray:Array = new Array ;
var previousLogos:Array = new Array ;
var xpoint:int;
var ypoint:int;
function getNewSymbols()
{
previousLogos = new Array ;
greystoneLogos = new Array ;
var i:int;
for (i=0; i < 3; i++)
{
greystoneLogos[i] = new GreystoneLogo1();
greystoneLogos[i].width = 100;
greystoneLogos[i].height = 60;
addSymbolToStage(greystoneLogos[i],i*2000);
}
}
getNewSymbols();
function addSymbolToStage(currentLogo:MovieClip,waitTime:int)
{
var i3:int;
var i4:int;
var logoBoundaries:Array = new Array()
var XandY:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo)
{
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
logoBoundaries = getOffDimensions(currentLogo)
for (i4=0; i4 < logoBoundaries.length; i4++)
{
XandY = logoBoundaries[i4].split(":")
while ((xpoint <= (Number(XandY[0]) + Number(currentLogo.width * 4.8)) && xpoint >= (Number(XandY[0]) - Number(currentLogo.width * 4.8))) && (ypoint <= (Number(XandY[1]) + Number(currentLogo.height * 6.9)) && ypoint >= (Number(XandY[1]) - Number(currentLogo.height * 6.9)))){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width * 4.8));
trace(XandY[0] + " And " + (Number(currentLogo.width * 4.8)))
trace(xpoint + " And " + (Number(XandY[0]) + Number(currentLogo.width * 4.8)))
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height * 6.9));
}
}
}
else
{
continue;
}
}
previousLogos.push(currentLogo);
currentLogo.x = xpoint;
currentLogo.y = ypoint;
stage.addChild(currentLogo);
currentLogo.gotoAndStop(1);
var countTime:Timer = new Timer(waitTime,1);
countTime.addEventListener(TimerEvent.TIMER, function(){
currentLogo.gotoAndPlay(1);
currentLogo.addFrameScript ( currentLogo.totalFrames - 1 , function(){
currentLogo.stop()
stage.removeChild(currentLogo)
if(stage.numChildren <= 1){
getNewSymbols();
}
}) ;
});
countTime.start();
}
function getOffDimensions(currentLogo:MovieClip){
var i3:int;
var tempArr:Array = new Array()
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo){
tempArr[i3]=greystoneLogos[i3].x +":"+ greystoneLogos[i3].y
}
}
return tempArr
}
function randomRange(max:Number, min:Number = 0):Number
{
return Math.random() * (max - min) + min;
}
There are also may be a handful of unused variables from multiple things I've been trying out.
The code that I posted, will make the movie clip appear at a random spot based on the last movie clip that came up. So let's say we have 3 movie clips (the user will be able to change how many of the clips get displayed) 1 appears at 0,0 the other at 400,400 and the last one appears at 10,10 because I have no way of saving the previous values to compare in the while loop.
I hope this clarifies it a tad more
EDIT:
Based on a function shown below, I've added this:
for (i3=0; i3 < greystoneLogos.length; i3++)
{
if (greystoneLogos[i3] !== currentLogo && greystoneLogos[i3] != null)
{
while(currentLogo.hitTestObject(greystoneLogos[i3]) == true){
xpoint = randomRange(this.stage.stageWidth - (currentLogo.width));
ypoint = randomRange(this.stage.stageHeight - (currentLogo.height));
i3 = 0
}
}else{
continue;
}
}
Which results in a rather bad loop as well as the logo's still overlap above each other
The quickest way I can think to solve this is to do a hit test (http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/DisplayObject.html#hitTestObject()) on the new MovieClip and if it returns true, run the placement code again
Ok, here's the implementation of this quickest way.
You can track your present displayObject by the .numChildren property. You don't even need an array of your logos or things. Let's say you have a function that adds new movieClip to your screen. It knows how many clips you can have (MovieClipDummy is just my testing class - it draws a circle with a specified radius. It should be replaced by your objects).
private function addAnotherMovieClip(): void {
if (_currentDummmiesCount < _dummmiesCount) {
var newDummy: MovieClipDummy = new MovieClipDummy(90);
addChild(newDummy);
//If we get a stackoverflow error (see below)
//we just remove our object from the screen as it will most likely just won't fit
if (checkForEmptySpace(newDummy) != "") {
removeChild(newDummy);
return;
}
} else {
//Do nothing or do something
}
}
It calls another function, checkForEmptySpace, which tries to place your movieclip so it won't overlap with other objects. Here it is:
private function checkForEmptySpace(newClip: Sprite): String {
//you should store your screen width somewhere.
//you can also call stage.stageWidth instead, but first make sure that you
//always have a link to the stage or it will throw an error
newClip.x = Math.random() * _screenWidth;
newClip.y = Math.random() * _screenHeight;
//===========================
//Important part - we try to check all the present children of our container
//numChildren is a property of the DisplayObjectContainer
for (var i: int; i < numChildren; i++) {
//We need a try here because we will get a StackOverflow error if there's no empty space left
try {
//We need to check if our current display object, received with getChildAt()
//is not the same as the one we've just added to the screen
//And if our new object intersects with ANY other object on the stage - we
//call THIS function once again.
//We do recursion because we can easily catch an error and remove this object from
//the screen
if (newClip != getChildAt(i) && newClip.hitTestObject(getChildAt(i))) {
//If our recursive function returns an error - we should pass it further
if (checkForEmptySpace(newClip) != "") {
return "error";
}
}
//The only error that can go here is stackoverflow error. So when we get one
//we return this error string
} catch (error: Error) {
trace(error);
return "error";
}
}
//We only return this empty string if we don't have stackoverflow error
//so there's possibly no space left for another movieclip
return "";
}
You can do it without recursion, but you will have to check for empty space with another logic.
And, you can do it more "professionally" by using Minkowski addition. You should consider storing an array of "boundary" points of your movieclips (let's say every movieclip is a rectangle) and when you add a new object to the screen, you calculate this Minkowsky addition. It will have some "free" spots on your screen which represent any possible coordinates of your new movieclip. It's pretty interesting to implement something like that because the accuracy will be phenomenal. But if you don't have time - just use that recursive placement function

timerEvent within enterFrame function?

I'm calling a timerEvent function from enterFrame, it's running the timerEvent function on everyFrame. Is there a way to control it?
I've got my bullets firing function with timerEvent of 500, so it shoots a bullet every half a second on 24fps. It's working fine for now. Now I want to change bullet speed and skin with respect to weapon.
////////////////////////////////
//this function is called first time within an EnterFrame function
///////////////////////////////
function weaponCheck():void
{
switch (weaponState)
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
laserTimer = new Timer(600);
laserTimer.addEventListener(TimerEvent.TIMER, timerListener);
laserTimer.start();
function timerListener(e:TimerEvent):void
{
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
function doubleGun():void
{
trace("Double gun");
doubleGunTimer = new Timer(400);
doubleGunTimer.addEventListener(TimerEvent.TIMER, timerListener2);
doubleGunTimer.start();
function timerListener2(e:TimerEvent):void
{
var tempLaser:MovieClip = new doubleG();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
{
removeChild(tempGunBlast);
}
}
}
///////////////////////////////////////////////////
//Following function is called within an enterFrame event function
/////////////////////////////////////////////////
function playGame():void
{
weaponCheck();
blah1();
blah2();
blah3();
testForEnd();
}
function testForEnd():void
{
if (level == 3)
{
laserTimer.stop();
weaponState = STATE_DOUBLE_GUN;
weaponCheck();
}
}
So when the game runs for first time, it works fine and uses the timer event of 600 to hit the bullets, but when level == 3 and weaponState changes, the 2nd firing function doubleGun(); is called but it starts to fire the bullets on a per frame count, not on a controlled timerEvent. Please Help. Thanks.
Why don't you drop timers and use enter frame listener as a manner to count time? You are already calling weaponCheck() from an enterframe listener. Make it so that the actual gun() and doublegun() calls will only generate animation, such as firin' mah lazers and blasts, and the main function will just count time.
function weaponCheck():void
{
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
this.reloading=0;
switch (weaponState) // and here we fire with the gun state
{
case STATE_GUN :
gun();
break;
case STATE_DOUBLE_GUN :
doubleGun();
break;
}
}
function gun():void
{
trace("single gun");
var tempLaser:MovieClip = new Laser();
var tempGunBlast:MovieClip = new Gun_blast_01();
tempLaser.x = player.x +((player.width/2)+12);
tempLaser.y = player.y;
tempGunBlast.x = stage.mouseX + 104;
tempGunBlast.y = tempLaser.y;
Lasers.push(tempLaser);
addChildAt(tempLaser,1);
addChildAt(tempGunBlast, 3);
}
And similarly double gun. FULLY_RELOADED is a constant, reloading is a variable used to track time, it should be a property of the one who's firing.
Note, this approach requires you to manage your "tempGunBlast"s elsewhere, perhaps in the very weaponCheck function, if so, modify it as follows:
function weaponCheck():void
{
if (tempGunBlast) if (tempGunBlast.currentFrame >= tempGunBlast.totalFrames)
removeChild(tempGunBlast);
this.reloading+=this.weaponFiringSpeed; // you alter this to make your weapon fire slower or faster
if (this.reloading<FULLY_RELOADED) return; // we are not yet ready to fire
... // rest of code unchanged
You will most likely not be able to copypastely implement this, but please try.

How can I get rid of non-null hitTestObject error with already instantiated objects?

I am a beginner/intermediate AS3 "programmer" trying to complete a skeeball-like flash-based game for a university assessment, and I'm doing myself serious mental damage trying to get even basic object (ball) collisions working with scoring targets on my stage. I'm using a power bar type variable to determine the force of the ball roll which is translated into a tween to create a smooth movement up my "lane" and into the scoring area (this is overhead perspective). The targets are instances of movie clips inside a larger movie clip that is made up of all the components of the game table. Even though my game table and scoring components have already been instantiated when I release the ball, I am getting the typical non-null errors:
ballSpeed is 552
targetArray value is [object upperScoringAreaCTarget_mc]
TypeError: Error #2007: Parameter hitTestObject must be non-null.
at flash.display::DisplayObject/_hitTest()
at flash.display::DisplayObject/hitTestObject()
at SkeeBlast_7_fla::MainTimeline/ballTargetScore()
at SkeeBlast_7_fla::MainTimeline/rollBall()
at SkeeBlast_7_fla::MainTimeline/releaseBall()
Here is my ball release function:
function releaseBall(event:MouseEvent):void
{
rollBall();
gameElements.removeEventListener(MouseEvent.MOUSE_MOVE, moveBall);
}
function rollBall():void
{
ballSpeed = rollPower * 12;
trace("ballSpeed is " + ballSpeed);
ballFriction();
ballGravity();
//ball.y -= ballSpeed;
//var myBallTween:Tween = new Tween(ball,"y",Strong.easeOut,ball.y,ball.y - ballSpeed,3,true);
myBallTween = new Tween(ball,"y",Strong.easeOut,ball.y,ball.y - ballSpeed,3,true);
myBallTween.start();
ballTargetScore();
}
and my collision detection and scoring function:
//match targets to scoring values which should be tied to determineScore()
function ballTargetScore():void
{
var targetValue:String;
var targetArray:Array = new Array(gameTable.upperScoringArea.upperScoringAreaCTarget,
gameTable.upperScoringArea.upperScoringAreaLtTarget,
gameTable.upperScoringArea.upperScoringAreaRtTarget,
gameTable.middleScoringArea.middleScoringAreaTargetTop,
gameTable.middleScoringArea.middleScoringAreaTargetMiddle,
gameTable.middleScoringArea.middleScoringAreaTargetLower,
gameTable.lowerScoringArea.lowerScoringAreaTarget);
if (ball.hitTestObject(gameTable.tableDisplay))
{
myBallTween.stop();
}
else
{
for (var i:uint; i < targetArray.length; i++)
{
if (targetArray[i] != null)
{
trace("targetArray value is " + targetArray[i]);
if (ball.hitTestObject(gameTable.upperScoringArea.upperScoringAreaCTarget))
{
targetValue = gameTable.upperScoringArea.upperScoringAreaC_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.upperScoringAreaLtTarget))
{
targetValue = gameTable.upperScoringArea.upperScoringAreaLt_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.upperScoringAreaRtTarget))
{
targetValue = gameTable.upperScoringArea.upperScoringAreaRt_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.middleScoringAreaTargetTop))
{
targetValue = gameTable.middleScoringArea.middleScoringAreaU_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.middleScoringAreaTargetMiddle))
{
targetValue = gameTable.middleScoringArea.middleScoringAreaM_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.middleScoringAreaTargetLower))
{
targetValue = gameTable.middleScoringArea.middleScoringAreaL_text.text;
}
if (ball.hitTestObject(gameTable.upperScoringArea.lowerScoringAreaTarget))
{
targetValue = gameTable.lowerScoringArea.lowerSA_text.text;
}
else
{
trace("no hit");
}
}
}
}
//gameElements.removeEventListener(Event.ENTER_FRAME, ballTargetScore);
determineScore(targetValue);
//return targetValue;
}
It's all still a bit messy as I tinker trying to find the right combination.
I need to first get a basic ball<->target or boundary collision working, but ultimately, I'd like to try to figure out how to control the collisions conditionally since many of the targets are aligned vertically like a skeeball table. Will I be able to measure the speed of the ball as it is tweening/rolling? Will I be able to apply gravity correctly so that the ball falls and if it catches one of the arcs beneath the scoring target, it will roll along the arc until it 'falls into the hole'? I think I will have issues with the bounding box of my objects on that one.
Thanks for any help,
Alan
Here's the response to Vesper with a cleaned up function (thanks) and new error.
function ballTargetScore():void
{
var targetValue:String;
var targetArray:Array = new Array(gameTable.upperScoringArea.upperScoringAreaCTarget,
gameTable.upperScoringArea.upperScoringAreaLtTarget,
gameTable.upperScoringArea.upperScoringAreaRtTarget,
gameTable.middleScoringArea.middleScoringAreaTargetTop,
gameTable.middleScoringArea.middleScoringAreaTargetMiddle,
gameTable.middleScoringArea.middleScoringAreaTargetLower,
gameTable.lowerScoringArea.lowerScoringAreaTarget);
var targetTextArray:Array = new Array(gameTable.upperScoringArea.upperScoringAreaC_text.text,
gameTable.upperScoringArea.upperScoringAreaLt_text.text,
gameTable.upperScoringArea.upperScoringAreaRt_text.text,
gameTable.middleScoringArea.middleScoringAreaU_text.text,
gameTable.middleScoringArea.middleScoringAreaM_text.text,
gameTable.middleScoringArea.middleScoringAreaL_text.text,
gameTable.lowerScoringArea.lowerSA_text.text);
for (var i:uint; i < targetArray.length; i++)
{
if (targetArray[i] != null)
{
trace("targetArray value is " + targetArray[i]);
if (ball.hitTestObject(targetArray[i]))
{
targetValue = targetTextArray[i];
trace('targetValue becomes',targetValue);
}
}
}
determineScore(targetValue);
}
and the error:
ballSpeed is 432
targetArray value is [object upperScoringAreaCTarget_mc]
targetArray value is [object upperScoringAreaLtTarget_mc]
targetArray value is [object upperScoringAreaRtTarget_mc]
targetArray value is [object middleScoringAreaTargetTop_mc]
targetArray value is [object middleScoringAreaTargetMiddle_mc]
targetArray value is [object middleScoringAreaTargetLower_mc]
targetArray value is [object lowerScoringAreaTarget_mc]
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
at flash.display::DisplayObjectContainer/removeChild()
at SkeeBlast_7_fla::MainTimeline/determineScore()
at SkeeBlast_7_fla::MainTimeline/ballTargetScore()
at SkeeBlast_7_fla::MainTimeline/rollBall()
at SkeeBlast_7_fla::MainTimeline/releaseBall()
Thanks for the assistance.
The rules, which I'm sure are necessary, are making me turn this into a really long question. :)
Anyway, I sort of figured out the error about the DisplayObject, but the program still isn't working correctly.
I fixed the error by adding my gameElements MC to my removeChild call in the determineScore function, which is below; however, the ball still isn't removed, even with the error gone, and the targetValue and the score never update (or show up in the trace from above).
function determineScore(scoreEvent:String):void
{
if ( scoreEvent == "D-O" || scoreEvent == "2XB" || scoreEvent == "?")
{
if (scoreEvent == "D-O")
{
ballCount += 1;
gameTable.tableDisplay.BR_text.text = ballCount - 1;
gameTable.tableDisplay.BR_text.embedFonts = false;
gameElements.removeChild(ball);
if (ballCount > 0 )
{
initBall();
}
else
{
drawEnd();
}
}
else if (scoreEvent == "2XB")
{
ballCount += 2;
gameTable.tableDisplay.BR_text.text = ballCount - 1;
gameTable.tableDisplay.BR_text.embedFonts = false;
gameElements.removeChild(ball);
if (ballCount > 0 )
{
initBall();
}
else
{
drawEnd();
}
}
else
{
determineScore(allRandomScore(allScoresArray));
}
}
else
{
scoreTotal += Number(scoreEvent);
ballScore = Number(scoreEvent);
gameTable.tableDisplay.Score_text.text = scoreTotal;
gameTable.tableDisplay.Score_text.embedFonts = false;
gameElements.removeChild(ball);
if (ballCount > 0 )
{
initBall();
}
else
{
drawEnd();
}
}
}
Thanks for looking at this. I feel like I'm finally making a little bit of progress.
Ok, continuing along with yet another update.
gameElements is the primary empty MC that I add all the other MC, like the game table and the ball, to so that when I go back to the main menu, I can remove everything at once.
var gameElements:MovieClip = new MovieClip();
var gameTable:MovieClip = new table_mc();
var ball:MovieClip = new blastBall();
and from my drawGame function:
...
stage.addChildAt(gameElements, 1);
gameElements.addChild(gameTable);
initBall();
...
and initBall:
function initBall():void
{
//resize ball
ball.height = 18;
ball.width = 18;
//place ball on table in correct location
ball.x = gameTable.x;
ball.y = gameTable.height - 20;
gameElements.addChild(ball);
//reduce number of remaining balls;
ballCount -= 1;
//hide the mouse and connect its movement to ball
Mouse.hide();
gameElements.addEventListener(MouseEvent.MOUSE_MOVE, moveBall);
}
Hope there's no limit to this entry. :)
Here's the little addition I put in determineScore to pick up the "results" from ballTargetScore (or lack thereof, really):
if (scoreEvent == null)
{
trace("scoreEvent is null");
gameTable.tableDisplay.BR_text.text = ballCount - 1;
gameTable.tableDisplay.BR_text.embedFonts = false;
gameElements.removeChild(ball);
if (ballCount > 0)
{
initBall();
}
else
{
drawEnd();
}
}
(haven't cleaned up anything else yet) Still trying to get that first collision to work. When I started catching the null value, initBall and drawEnd sort of started to work (ok, it still doesn't really do what I want but at least there was a response).
Well, you have an array of your targets, and the error you receive means that some of its elements are null. But, when you iterate through the array, you for some reason check against the entire set of targets, not checking if any of them are null. I don't understand why. Seeing that you need a text variable to be assigned if a hit is detected, I advise you making another array with corresponding indexes that will contain all the texts you want to assign.
var targetTextArray:Array=(gameTable.upperScoringArea.upperScoringAreaC_text.text,
gameTable.upperScoringArea.upperScoringAreaLt_text.text,
gameTable.upperScoringArea.upperScoringAreaRt_text.text,
gameTable.middleScoringArea.middleScoringAreaU_text.text,
gameTable.middleScoringArea.middleScoringAreaM_text.text,
gameTable.middleScoringArea.middleScoringAreaL_text.text,
gameTable.lowerScoringArea.lowerSA_text.text);
Then, when you iterate through targetArray, you no longer need to check what was that target in order to retrieve the correct text. You do:
for (var i:uint; i < targetArray.length; i++)
{
if (targetArray[i] != null)
{
trace("targetArray value is " + targetArray[i]);
if (ball.hitTestObject(targetArray[i])) {
targetValue=targetTextArray[i];
trace('targetValue becomes',targetValue);
}
}
}
Update: Okay, let's clean up your determineScore() function. It looks like you are correctly adding and removing your ball, but it's possible you do something of this twice. Removing the ball twice without adding it back will net you this error. Currently I can't get if gameElements.removeChild(ball); gets called twice within that function.
function determineScore(scoreEvent:String):void
{
var reBall:Boolean=false; // should we make another ball roll, given the score
var reDisplay:Boolean=false; // should we set new text
if ((scoreEvent==null)||(scoreEvent=="")) return; // we haven't scored
if ( scoreEvent == "D-O" || scoreEvent == "2XB" || scoreEvent == "?")
{
if (scoreEvent == "D-O")
{
ballCount += 1;
reBall=true;
reDisplay=true;
}
else if (scoreEvent == "2XB")
{
ballCount += 2;
reBall=true;
reDisplay=true;
}
else
{
determineScore(allRandomScore(allScoresArray));
// hey, what are we doing here? We seemingly receive a "?" as an input,
// and we roll a random value out of a set given elsewhere. Okay
return; // as good measure, because we don't have to do anything more
}
}
else
{ // if we're here, we assume we have a numeric score passed inside, right?
scoreTotal += Number(scoreEvent);
ballScore = Number(scoreEvent);
reBall=true;
reDisplay=true;
}
// Now look, we always have to launch a new ball or display end, and
// we always have to redraw score textfield. But look, you are always calling
// determineScore with some value, while you should do it only if you hit something!
// So I added starting clause of "if null or empty string".
if (reDisplay) {
// we have to update text
gameTable.tableDisplay.BR_text.text = ballCount - 1;
gameTable.tableDisplay.BR_text.embedFonts = false;
}
if (reBall) {
// we have to remove ball and launch another, if we have any left
gameElements.removeChild(ball);
if (ballCount > 0 )
{
initBall();
}
else
{
drawEnd();
}
}
}