AS3-太空射手敌人射击



我对AS3和编程很陌生,我一直在AS3中开发太空射击游戏,遇到了敌人射击的麻烦。敌人从屏幕顶部垂直向下飞行,一旦他们的y坐标等于玩家的y坐标,就应该发射两枪(一枪向左,一枪向右)。这很好,除了在30秒到2分钟之后,会出现以下错误。

TypeError:错误#1009:无法访问null对象引用的属性或方法。在EnemyShip2/fireWeapon()[G:\Games Related\1942\src\EnemyShip2.as:78]在EnemyShip2/loop2()[G:\Games Related\1942\src\EnemyShip2.as:65]

TypeError:错误#1009:无法访问null对象引用的属性或方法。在EnemyShot/removeSelf()[G:\Games Related\1942\src\EnemySshot.as:43]在EnemyShot/loop()[G:\Games Related\1942\src\EnemySshot.as:36]

以下是敌方舰艇等级和敌方射击等级的相关代码。

敌舰

package 
{
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.Stage;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.utils.Timer;
    /**
     * ...
     * @author D Nelson
     */
    [Embed(source = "../assets/enemyship2.png")]
    //This enemy moves relatively slowly and fires horizontal shots
    public class EnemyShip2 extends Bitmap
    {
        private var vy:Number = 3;
        //private var ay:Number = .2;
        private var target:HeroShip;
        private var enemyShip2:EnemyShip2;
        private var enemyfireTimer:Timer;
        private var enemycanFire:Boolean = true;
        public function EnemyShip2(target:HeroShip):void
        {
            this.target = target;
            scaleX = 0.3;
            scaleY = 0.3;
            x = Math.floor(Math.random() * 550);
            y = -105;
            addEventListener(Event.ENTER_FRAME, loop2, false, 0, true);
            enemyfireTimer = new Timer(1000, 1);
            enemyfireTimer.addEventListener(TimerEvent.TIMER, handleenemyfireTimer, false, 0, true);
        }
        private function handleenemyfireTimer(e:TimerEvent) : void
        {
            //the timer runs, so a shot can be fired again
            enemycanFire = true;
        }

        private function removeSelf2():void 
        { 
            removeEventListener(Event.ENTER_FRAME, loop2);
            if (stage.contains(this))
            {
                stage.removeChild(this);
            }
        }   
        private function loop2 (e:Event):void
        {
            //vy += ay;
            y += vy;
            if (y > stage.stageHeight)
            {
                removeSelf2();
            }
            if (y >= target.y && (enemycanFire))
            {
                fireWeapon();
                enemycanFire = false;
                enemyfireTimer.start();
            }
            if (this.x > 540 || this.x < 10)
            {
                removeSelf2();
            }
        }
        private function fireWeapon():void
        {
            stage.addChild(new EnemyShot(target, x, y, -4));
            stage.addChild(new EnemyShot(target, x, y, +4));
        }
    }
}

敌人射击

package 
{
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.events.Event;
    import flash.display.Stage;
    /**
     * ...
     * @author D Nelson
     */
    [Embed(source="../assets/enemyshot.png")]
    public class EnemyShot extends Bitmap
    {
        private var speed:Number;
        private var target:HeroShip;
        private var vx:Number;
        public function EnemyShot(target:HeroShip, x:Number, y:Number, vx:Number) 
        {
            this.target = target;
            this.x = x;
            this.y = y;
            this.vx = vx;
            scaleX = 0.3;
            scaleY = 0.3; 
            addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
        }
        private function loop(e:Event) : void
        {
            x += vx;
            if (x >= 600 || x <= -50) 
            {               
                removeSelf();
            }
        }
        private function removeSelf():void 
        { 
            removeEventListener(Event.ENTER_FRAME, loop); 
            if (stage.contains(this))
            {
                stage.removeChild(this);
            }
        }    
    }
}

还提供了主类,以防有任何帮助。

package 
{
    import flash.display.Sprite;
    import flash.display.MovieClip;
    import flash.display.DisplayObject;
    import flash.display.Stage;
    import flash.events.*;
    import flash.events.Event;  
    import flash.events.EventDispatcher;
    import flash.events.KeyboardEvent;
    import flash.events.TimerEvent;
    import flash.ui.Mouse;
    import flash.utils.*;
    import flash.utils.ByteArray;
    import flash.utils.Timer;
    import flash.text.*;
    import flash.media.Sound;
    import flash.media.SoundChannel;
    /**
     * ...
     * @author D Nelson
     */
    public class Main extends MovieClip
    {
        [Embed(source = "snd/gamemusic2.mp3")]
        private var MySound : Class;         
        private var mainsound : Sound;
        private var shootsound: Sound = new ShootSound;
        private var sndChannel:SoundChannel = new SoundChannel;
        public var heroShip:HeroShip;
        public var enemyShip1:EnemyShip1
        public var enemyShip2:EnemyShip2
        public var enemyShip3:EnemyShip3
        public var enemyShip4:EnemyShip4
        public var bossShip: BossShip
        public var enemyShot: EnemyShot;
        public var playerShot: PlayerShot;
        private var background1:Background1;
        private var background2:Background2;
        public static const scrollspeed:Number = 2;
        public var playerShotArray:Array = new Array()
        public var enemyShotArray:Array = new Array()
        public var enemies1Array:Array = new Array()
        public var enemies2Array:Array = new Array()
        private var fireTimer:Timer; //this creates a delay between each shot
        private var canFire:Boolean = true; //this checks if a shot can be fired                
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
            mainsound = (new MySound) as Sound;
            shootsound = (new ShootSound) as Sound;
            mainsound.play();
            background1 = new Background1();
            background2 = new Background2();
            //setting the backgrounds one below another
            background1.y = 0;
            background2.y = background1.height;
            //add background at the lowest depth level
            stage.addChildAt(background1,0);
            stage.addChildAt(background2, 0);           
            //sets up the timer and its listener
            fireTimer = new Timer(250, 1);
            fireTimer.addEventListener(TimerEvent.TIMER, handlefireTimer, false, 0, true);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, handleCharacterShoot);
            //background scrolling effect
            stage.addEventListener(Event.ENTER_FRAME, backgroundScroll);
            //loop for enemy1 variety
            stage.addEventListener(Event.ENTER_FRAME, loop1, false, 0, true);
            //loop for enemy2 variety
            stage.addEventListener(Event.ENTER_FRAME, loop2, false, 0, true);
            //speed of player shots
            setInterval(playerShootMovement, 10);           
        }
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            //entry point           
            heroShip = new HeroShip();
            stage.addChild(heroShip);
            //test values for enemies
            /*enemyShip1 = new EnemyShip1();
            stage.addChildAt(enemyShip1, 2);
            enemyShip1.y = stage.stageWidth * 0.3;
            enemyShip1.x = 300;
            enemyShip2 = new EnemyShip2();
            stage.addChildAt(enemyShip2, 2);
            enemyShip2.y = stage.stageWidth * 0.3;
            enemyShip2.x = 200;
            enemyShip3 = new EnemyShip3();
            stage.addChildAt(enemyShip3, 2);
            enemyShip3.y = stage.stageWidth * 0.3;
            enemyShip3.x = 100;
            enemyShip4 = new EnemyShip4();
            stage.addChildAt(enemyShip4, 2);
            enemyShip4.y = stage.stageWidth * 0.3;
            enemyShip4.x = 400;
            bossShip = new BossShip();
            stage.addChildAt(bossShip, 1);
            bossShip.y = 10;
            bossShip.x = 130;*/         
            Mouse.hide();           
        }
        private function handlefireTimer(e:TimerEvent) : void
        {
            //the timer runs, so a shot can be fired again
            canFire = true;
        }
        public function playerShoot():void
        {
            //if canFire is true, allow a shot to be fired, then set canFire to false and start the timer again
            //else, do nothing          
            if (canFire)
            {
                //Add new line to the array
                playerShotArray.push(playerShot = new PlayerShot);
                //Spawn missile in ship
                playerShot.y = heroShip.y;
                playerShot.x = heroShip.x+11;
                addChild(playerShot);
                canFire = false;
                fireTimer.start();              
                sndChannel = shootsound.play();
            }
        }
        public function playerShootMovement():void
        {
            //code adapted from the Pie Throw tutorial
            for (var i:int = 0; i < playerShotArray.length; i++) 
            {
                playerShotArray[i].y -= 10;
                if (playerShotArray[i].y == 850)
                {
                    playerShotArray.shift();
                }
            }           
        }
        public function handleCharacterShoot(e:KeyboardEvent):void
        {
            /** 
             * SpaceBar = 32
             */                                 
            if (e.keyCode == 32)
            {
                playerShoot();
            }           
        }       
        public function backgroundScroll (evt:Event):void
        {
            background1.y += scrollspeed;
            background2.y += scrollspeed;
            if (background1.y >= stage.stageHeight)
            {
                //the background is below the visible stage area, put it above the other background       
                background1.y = background2.y - background2.height;
            }
            else if (background2.y >= stage.stageHeight)
            {
                background2.y = background1.y - background2.height;
            }           
        }
        //EnemyShip 1 spawning
        private function loop1(e:Event):void
        {
            //generates probability for the ship to spawn (lower number to increase odds, decrease it to decrease odds)
            if (Math.floor(Math.random() * 55) == 5)
            {
                var enemyShip1:EnemyShip1 = new EnemyShip1(heroShip); 
                enemyShip1.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy1, false, 0, true);
                enemies1Array.push(enemyShip1); 
                stage.addChild(enemyShip1);
            }
        }
        private function removeEnemy1(e:Event):void
        {
            //removes the enemy that most recently left the screen from the array
            enemies1Array.splice(enemies1Array.indexOf(e.currentTarget), 1);
        }
        //EnemyShip2 spawning
        private function loop2(e:Event):void
        {
            if (Math.floor(Math.random() * 40) == 5)
            {
                var enemyShip2:EnemyShip2 = new EnemyShip2(heroShip); 
                enemyShip2.addEventListener(Event.REMOVED_FROM_STAGE, removeEnemy2, false, 0, true);
                enemies2Array.push(enemyShip2); 
                stage.addChild(enemyShip2);
            }
        }
        private function removeEnemy2(e:Event):void
        {
            enemies2Array.splice(enemies2Array.indexOf(e.currentTarget), 1);
        }
    }
}

起初,我认为这是因为敌人在离屏幕两侧太远的情况下开枪,但事实并非如此。如有任何帮助,我们将不胜感激。

每次删除对象时,都需要确保删除ENTER_FRAME侦听器。

实际上,我的建议是不要在整个游戏对象中添加ENTER_FRAME处理程序。这会变得很难管理,并导致您遇到的错误。相反,添加一个ENTER_FRAME作为核心游戏循环,并通过调用主游戏类中维护的游戏对象列表上的update()函数来更新对象。当您移除一个对象时,您就不会再调用update()了。只需移除ENTER_FRAME处理程序就可以很容易地暂停游戏。

例如,这里有一个我喜欢用于简单游戏的模式:

interface IGameObject {
    update():void;
}
class Enemy extends Sprite implements IGameObject {
    public update():void {
        // move, fire, etc
    }
}
class Player extends Sprite implements IGameObject {
    public update():void {
        // move, fire, etc
    }
}
class Bullet extends Bitmap implements IGameObject {
    public update():void {
        // move, collide, etc
    }
}
class Main extends Sprite {
    private objects:Vector.<IGameObject> = new <IGameObject>[];
    public start():void {
        addEventListener(Event.ENTER_FRAME, update);
    }
    public stopGame():void {
        removeEventListener(Event.ENTER_FRAME, update);
    }
    private function update(e:Event):void {
        for each (var object:IGameObject in objects) {
            object.update();
        }
    }
    public addObject(object:IGameObject):void {
        objects.push(object);
        addChild(object as DisplayObject);
    }
    public removeObject(object:IGameObject):void {
        objects.splice(objects.indexOf(object), 1);
        removeChild(object as DisplayObject);
    }
}

使用addObjectremoveObject添加和删除对象。您可以通过对Main的引用或通过从对象中调度的事件处理程序来调用它们。

最新更新