access a movie clip on a certain frame within an array as3 - arrays

I have movie clip in an array (newStep) that is being added to the stage dynamically. It's randomly choosing a frame to go to every time an instance is added. There is a nested movie clip (stepLine) that i need to change the alpha of. This code actually works for adding a string to to the dynamic text box (pointsDText) but when i try to access the nested movie clip (stepLine) it gives me 1009 null object reference error. The funny thing is the code actually works and does change the alpha of the movie clip but i still get that error, and i think it's making my game more glitchy. I've tried using if(contains(steps[r].stepLine)) too but it doesn't work. Is there a better way to access this movie clip without getting the error?
if(newStep != null){
for(var r:int = 0; r<steps.length;r++){
if(steps[r].currentLabel == "points"){
steps[r].pointsDText.text = String(hPoints);
}
if(steps[r].currentLabel == "special"){
steps[r].stepLine.alpha = sStepAlpha;
}
if(steps[r].currentLabel == "life"){
steps[r].stepLine.alpha = hStepAlpha;
}
}
}
This is so difficult to explain but i hope you understand.
Thanks so much.

A null reference error happens when you try to access properties of a variable that doesn't point to any object -- a null reference. You're effectively trying to access an object that doesn't exist. For example, perhaps stepLine doesn't exist in one of those instances, so stepLine.alpha is causing the error. (How do you set the alpha of a non-existent clip?) Maybe the steps[r] clip is on a frame where there is not yet any stepLine MovieClip.
You should run the movie in debug mode by pressing Ctrl+Shift+Enter in the Flash IDE. This should show you the exact line that causes the error, and it will let you inspect the values of any variables at that point. This should help you track down the problem. Similarly, you can use trace statements to aid in debugging. For example, you could trace(steps[r].stepLine); to check for null values, or even simply if(!steps[r].stepLine) trace("ERROR");. Additionally, if you wrap your accesses in if statements, you can avoid the null reference errors, even though this doesn't really address the underlying problem:
if(newStep != null){
for(var r:int = 0; r<steps.length;r++){
// only touch things if the movieclip actually exists
if(steps[r] && steps[r].stepLine){
if(steps[r].currentLabel == "points"){
steps[r].pointsDText.text = String(hPoints);
}
if(steps[r].currentLabel == "special"){
steps[r].stepLine.alpha = sStepAlpha;
}
if(steps[r].currentLabel == "life"){
steps[r].stepLine.alpha = hStepAlpha;
}
}
}
}

Related

Checking shot collisions and removing shot and target in AS3

Last year was my first successful year in teaching my students to create catch and avoid games in Flash with AS3. This year is getting better. Each year I come here for help at least once.
I'm trying to add shooting into the possibilities for their second game project. I can make the shot happen from the ship, gun, whatever, and make it move, and get rid of it when it is off screen, but have not figured out a clean way to have both the shot and the target go away (removeChild and array.splice) upon collision.
The code I have sort of works, but I keep getting the error,
TypeError: Error #1010: A term is undefined and has no properties. at
DropShootV02_fla::MainTimeline/checkShots()
.
Normally I know that this is because of a mismatch between objects and index numbers, but this is related to the call to a second array in removing boxes and bullets.
For simplicity I'll just include the shooting code. Similar organization creates and drops the boxes.
Any help is appreciated. BTW we are not using external script in an AS file.
var shotSpeed = 18;
var shots:Array = new Array();
import flash.events.MouseEvent;
import flash.events.Event;
stage.addEventListener(MouseEvent.CLICK, fireLaser);
function fireLaser(e:MouseEvent):void
{
if (gameOn==true)
{
var shot:Shot=new Shot();
addChild(shot);
shots.push(shot);
shot.gotoAndStop(1);
shot.x = user.x;
shot.y = user.y;
trace(shots.length);
}
}
addEventListener(Event.ENTER_FRAME, moveShot);
function moveShot(e:Event):void
{
for (var i:int=shots.length-1; i>=0; i--)
{
shots[i].y -= shotSpeed;
if (shots[i].y < -25)
{
removeChild(shots[i]);
shots.splice(i,1);
}
}
}
addEventListener(Event.ENTER_FRAME, checkShots);
function checkShots(e:Event):void
{
for (var i:int=shots.length-1; i>=0; i--)
{
for (var k:int=boxes.length-1; k>=0; k--)
{
if (shots[i].hitTestObject(boxes[k]))
{
if (boxes[i].type == "good")
{
score--;
scoreDisplay.text = "Score:" + score;
}
if (boxes[i].type == "bad")
{
score++;
}
removeChild(boxes[k]);
boxes.splice(k,1);
//if I leave this part out I get no errors,
//but but the bullet goes on to strike again
removeChild(shots[i]);
shots.splice(i,1);
}
}
}
}
Thanks kaarto:
I had tried that previously and kept getting the same error. I used that elsewhere in this game code. Turns out I needed to moderate how often the player was shooting. I changed from shooting with the mouse to using space instead and now the problem is gone. Break is definitely a good one here.
Merge move shot and checkShots in one ENTER_FRAME handler.

Splint warns undefined storage. Sanity check please?

I'm working on a game at the moment, and I'm having an issue with splint on the following code to add new enemy structs to my linked list.
void generate_enemy(enemy_struct* enemy)
{
enemy_struct* new_enemy;
// Make sure the incoming enemy isn't null.
if(enemy == NULL)
{
return;
}
// Run through till we find the last enemy in the list.
while(NULL != enemy->next_enemy)
{
enemy = enemy->next_enemy;
}
// Create a new enemy and point the last enemy to it.
new_enemy = malloc(sizeof(enemy_struct));
// If we're out of memory, don't bother making the new enemy.
if(NULL == new_enemy)
{
return;
}
else
{
// Initialise the new enemy.
new_enemy->location = 1;
new_enemy->motion = ENEMY_STATIC;
new_enemy->dead = false;//true;
// Ensure it carries the last enemy flag.
new_enemy->next_enemy = NULL;
// Put new enemy in previous enemy.
enemy->next_enemy = new_enemy;
}
return;
}
Splint gives the warning:
enemy.c: (in function generate_enemy)
enemy.c:57:12: Storage *(enemy->next_enemy) reachable from parameter contains 4
undefined fields: location, motion, dead, next_enemy
Storage derivable from a parameter, return value or global is not defined.
Use /*#out#*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)
Line 57 is the last return in the function.
Now, I'm fairly confident my code cannot return undefined values, as I either set all fields, or don't change anything and drop out.
Is there some case I'm missing out where I send back stuff undefined? If not, what's the best way to stop splint giving this warning?

Error while using removeChild() and accessing members of array

I am stuck doing this even though I know it's very simple. Yet, I am getting errors.
What I have:
I have 3 arrays.
1st Array contains objects of UpgradeButton class.
2nd Array contains objects of BuyButtonclass.
3rd Array named newCostlyShops contains Numbers.
BuyButton class and UpgradeButton class, both have a shopCode member which is a number; the number which I'm trying to equate.
What I'm trying to do:
My goal is to first look for BuyButton and UpgradeButton objects in the respective arrays which have shopCodes same as those in newCostlyShops.
After that, I removeChild() that object and splice it out from the array.
My Code:
Array 3:
var newCostlyShops:Array = new Array();
newCostlyShops = Object(root).WorkScreen_mc.returnCostlyShops();
trace(newCostlyShops); // this is tracing out the exact shopCodes I want and is working fine.
Deletion and Splicing codes:
for (looper = 0; looper < upgradeButtonsArray.length; looper++) {
for (var secondLooper: int = 0; secondLooper < newCostlyShops.length; secondLooper++) {
if (upgradeButtonsArray[looper].shopCode == newCostlyShops[secondLooper]) {
trace(looper);
trace(upgradeButtonsArray[looper]);
removeChild(upgradeButtonsArray[looper]);
upgradeButtonsArray.splice(looper, 1);
}
}
}
for (looper = 0; looper < buyButtonsArray.length; looper++) {
for (secondLooper = 0; secondLooper < newCostlyShops.length; secondLooper++) {
if (buyButtonsArray[looper].shopCode == newCostlyShops[secondLooper]) {
trace(looper);
trace(buyButtonsArray[looper]);
removeChild(buyButtonsArray[looper]);
buyButtonsArray.splice(looper, 1);
}
}
}
What's wrong with this Code:
I keep getting error
TypeError: Error #1010: A term is undefined and has no properties.
This error comes only after the 1st time this code is run and not the first time it is run. When I remove the removeChild and splice , this traces out objects that are not null, ever. Even after this whole function is called 100 times, the error is not shown. Only when I removeChild and use splice this occurs.
Is there something wrong with what I'm doing? How to avoid this error? This is throwing the whole program haywire. If there is any other alternative to this method, I'm open to take those methods as well as long as I don't get errors and my goal is reached.
It might sounds funny, but.... try to decrement looper after splicing.
trace(looper);
trace(upgradeButtonsArray[looper]);
removeChild(upgradeButtonsArray[looper]);
upgradeButtonsArray.splice(looper, 1);
looper--;
I think after splicing the array all item's are being shifted and you're skipping next one.
Also, you should get some more information with this error, like which class/line is throwing it. Maybe you need to enable "permit debugging" or something?
Bonus suggestion:
For newCostlyShops use Dictionary instead of Array so you won't have to nest for inside for...

Instantiation attempted on a non-constructor

I'm trying to make a bomb catching game (I'm actually using the code from the AS3 Classroom in a Book on arrays). As soon as I changed the word fruit to bomb in the code I got error 1007. With the exception of changing basket_mc to eod_mc and fruit to bomb (I used command-F and replaced with case sensitive on) I haven't changed much. It worked with eod_mc, but doesn't with bomb.
var bombArray:Array = new Array(bomb);
var bombsOnstage:Array = new Array();
var bombsCollected:int = 0;
var bombsLost:int = 0;
for (var i:int = 0; i<20; i++) {
var pickBomb = bombArray[int(Math.random() * bombArray.length)];
var bomb:MovieClip = new pickBomb();
addChild(bomb);
bomb.x = Math.random() * stage.stageWidth-bomb.width;// bomb.width is subtracted from the random x position to elimate the slight possibility that a clip will be placed offstage on the right.
bomb.y = Math.random() * -500;
bomb.speed = Math.random() * 15 + 5;
bombsOnstage.push(bomb);
}
eod_mc.addEventListener(MouseEvent.MOUSE_DOWN, dragEod);
stage.addEventListener(MouseEvent.MOUSE_UP, dragStop);
function dragEod(e:Event):void {
eod_mc.startDrag();
}
function dragStop(e:Event):void {
eod_mc.stopDrag();
}
stage.addEventListener(Event.ENTER_FRAME, catchBomb);
function catchBomb(e:Event):void {
for (var i:int = bombsOnstage.length-1; i > -1; i--) {
var currentBomb:MovieClip = bombsOnstage[i];
currentBomb.y += currentBomb.speed;
if (currentBomb.y > stage.stageHeight - currentBomb.height) {
currentBomb.y = 0 - currentBomb.height;
bombsLost++;
field2_txt.text = "Total Bombs Detonated: " + bombsLost;
}
if (currentBomb.hitTestObject(eod_mc)) {
bombsCollected++;
removeChild(currentBomb);
bombsOnstage.splice(i,1);
field1_txt.text = "Total Bombs Caught: " + bombsCollected;
if (bombsCollected >= 20) {
eod_mc.gotoAndStop(20);
} else if (bombsCollected > 15) {
eod_mc.gotoAndStop(15);
} else if (bombsCollected>10) {
eod_mc.gotoAndStop(10);
} else if (bombsCollected>5) {
eod_mc.gotoAndStop(5);
}
}
}
if (bombsOnstage.length <= 0) {
field1_txt.text = "You Win! You have defused the bombs.";
field2_txt.text = "";
stage.removeEventListener(Event.ENTER_FRAME, catchBomb);
}
if (bombsLost >= 20) {
field1_txt.text = "Sorry you lose. You have lost your foot!";
field2_txt.text = "";
stage.removeEventListener(Event.ENTER_FRAME, catchBomb);
for (var j:int = bombsOnstage.length-1; j > -1; j--) {
currentBomb = bombsOnstage[j];
removeChild(currentBomb);
bombsOnstage.splice(j,1);
}
}
}
Maybe to avoid making your programming life more twisted than it is now...
There are objects and classes in programming, a class is a description of a set of objects, say "table" (or, as it's better to differ in names for classes and variables, "Table", first letter capitalized) is a name of a class. An instance or an object is a structure that belongs to one or more classes, with Object being the topmost, as everything in programming is either an object or a "simple variable", that is, a number, a true/false, a string of characters (these are object types in AS3 too, though, Number, Boolean, String, but these generally not need to be instantiated via new) or probably some other simple type I don't remember right now.
Classes have properties and methods. A property is something that can be requested off any object of the class, say "height" for tables. Properties can be of any type, including nested objects, depending on what your base class is. Say stage in AS3 is a property of any DisplayObject which is used to get the only Stage object there is at runtime[1]. Methods are what any object of a class can be told to do. Say, bombs fall, explode, MovieClips can be told to stop(), etc. You write class code keeping in mind that all of the objects of this class will have to behave exactly like you've written, but since they can differ in properties, you can give them conditional behavior. For example, if a bomb has already exploded, it cannot blow up once more.
A variable, whether a property or a standalone var (if you declare one in a function) is a reference to an object of a given type. Say var i:int refers to some kind of an integer. Simple type vars are containers instead, that is, i=2; will place a 2 in the referred integer, and i=j; will copy the value from j into i, while var theStage:Stage=this.stage will instead create a reference to an existing object, and if that object will change, the reference will give you the changed object instead of its previous state. An array is a collection of variables, reachable by indexes, in AS3 they don't have to be of one type, and a Vector is a typed array.
Next, the lifetime of objects. An object only lives while there's an active reference to it, whether in a property of another alive object, or in a visible variable, or in an event listener (AS3 specific). An object is created via new ClassName(<parameters>), lives while you can reach it somehow, and is destroyed once you have no active links to it and Flash player decides to run garbage collector. Prior to this programmers had to deallocate objects themselves, a rudiment can be seen at BitmapData.dispose(). So, in order to avoid Flash player to run out of free memory, take full control over creation and destruction of links. You don't need to care for simple vars, they are being cared for by Flash player.
A lot of basic functions for interactions has already been implemented in AS3, look for them and examples of how they work in Adobe's manual, navigate through packages in the lower left, most of the interactive stuff is in flash.display package, refer to other packages as necessary.
Hope this will bring you some insight into programming basics.
[1]: There is one stage unless you're doing a load of an SWF, then there could be more, one Stage per one SWF.

SL Teleporting Multi-User Door?

I'm trying to make the following script only work when a user in a Certain Group can teleport to the specified x,y,z coordinate.
Source: www.heatonresearch.com
// Scripting Recipes for Second Life
// by Jeff Heaton (Encog Dod in SL)
// ISBN: 160439000X
// Copyright 2007 by Heaton Research, Inc.
//
// This script may be freely copied and modified so long as this header
// remains unmodified.
//
// For more information about this book visit the following web site:
//
// http://www.heatonresearch.com/articles/series/22/
vector target=<190, 197, 64>;
vector offset;
default
{
moving_end()
{
offset = (target- llGetPos()) * (ZERO_ROTATION / llGetRot());
llSitTarget(offset, ZERO_ROTATION);
}
state_entry()
{
llSetText("Teleport pad",<0,0,0>,1.0);
offset = (target- llGetPos()) * (ZERO_ROTATION / llGetRot());
llSetSitText("Teleport");
llSitTarget(offset, ZERO_ROTATION);
}
changed(integer change)
{
if (change & CHANGED_LINK)
{
llSleep(0.5);
if (llAvatarOnSitTarget() != NULL_KEY)
{
llUnSit(llAvatarOnSitTarget());
}
}
}
touch_start(integer i)
{
llSay(0, "Please right-click and select Teleport");
}
}
The teleport script uses two global variables. They are.
vector target=<190, 197, 64>;
vector offset; The target is the coordinate that the teleport script should send the user to. The offset is calculated based on the target and the current position of the teleporter. The offset is the distance that must be traveled to reach the target, starting from the teleporter.
Whenever the teleport pad is moved, the offset must be recalculated. The sit target is then updated.
moving_end()
{
offset = (target- llGetPos()) *
(ZERO_ROTATION / llGetRot());
llSitTarget(offset, ZERO_ROTATION);
} Likewise, when the teleport pad is first created, the offset must be recalculated. Additionally, the sit text is specified. Rotation is also taken into account and neutralized.
state_entry()
{
llSetText("Teleport pad",<0,0,0>,1.0);
offset = (target- llGetPos()) *
(ZERO_ROTATION / llGetRot());
llSetSitText("Teleport");
llSitTarget(offset, ZERO_ROTATION);
} When a user sits on the teleport pad, their avatar sits at the target location. The avatar is then stood up.
changed(integer change)
{
if (change & CHANGED_LINK)
{
llSleep(0.5);
if (llAvatarOnSitTarget() != NULL_KEY)
{
llUnSit(llAvatarOnSitTarget());
}
}
}
Ideas?
I haven't worked in SecondLife for a while, but isn't sit target maximized to 10 meters distance, anyway? And can't people just as easily use sit targets to get past walls and into areas that they shouldn't be in? The best way to do this is not to use scripts (because they can always be bypassed, even push scripts for area security and the like), and instead to just use SecondLife's built-in land security for your plot. Just don't allow anybody except your group to access that parcel at all.
If you really want to do it your way, the function you're looking for is llSameGroup. Just make sure to assign the proper group to your object, then llSameGroup(key id) will return whether or not the passed id is in the same group as the object.
Because SecondLife sucks in many ways in terms of object access and catching events, if I remember correctly you're going to have to keep the sit target in the wrong place at first, then only move it to the correct location if the user sitting is in the same group. Otherwise, the best you can do is have the user sit on it, and because the target is moved already, by the time your script kicks them off your teleporter they will already have teleported where you don't want them to go.
A better option might be to make a teleporter that doesn't use sit target, but actually moves itself to wherever the target location is. That way, you can make it so that it simply doesn't move unless someone of the same group is sitting in it. Doing that is very very simple.
vector targetPos = <100,100,100>;
vector originPos;
default
{
state_entry()
{
originPos = llGetPos();
}
changed(integer type)
{
if(type & CHANGED_LINK && llGetAvatarOnSitTarget() != NULL_KEY)
{
llSetTimerEvent(0.1);
llWhisper(0,"Going up!");
}
}
timer()
{
key sitter = llAvatarOnSitTarget();
//If someone is not sitting here, go back home.
if (sitter == NULL_KEY)
{
llSetPos(originPos);
//If we've reached the origin, stop the timer.
if (llGetPos() == originPos)
{
llSetTimerEvent(0.0);
}
}
//If someone is sitting here, go towards the teleport.
else
{
//Before we move, make sure that they're in our group.
if (llSameGroup(sitter))
{
llSetPos(targetPos);
}
else
{
llUnsit(sitter);
llWhisper(0,"Get off me, man!");
}
//If we've reached the target, unsit the sitter.
if (llGetPos() == targetPos)
{
llUnsit(sitter);
llWhisper(0,"We've arrived!");
}
}
}
}
I just wrote that from scratch after having not played SL for more than a couple years, so please let me know everyone if you spot errors. :-)
Yeah, there appears to be an error somewhere. I'm pretty sure all brackets are closed so it's most likely in the body of the script. I'll keep looking, let you know if I spot it.

Resources