AS3 - Space 射击敌人射击

AS3 - Space Shooter Enemy Shots

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

TypeError: Error #1009: Cannot access a property or method of a null object reference. at EnemyShip2/fireWeapon()[G:\Games Related42\src\EnemyShip2.as:78] at EnemyShip2/loop2()[G:\Games Related42\src\EnemyShip2.as:65]

TypeError: Error #1009: Cannot access a property or method of a null object reference. at EnemyShot/removeSelf()[G:\Games Related42\src\EnemyShot.as:43] at EnemyShot/loop()[G:\Games Related42\src\EnemyShot.as:36]

下面是相关代码,针对敌舰class以及敌人射击class。

敌舰

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);
            }
        }    
    }
}

还提供了主要内容 class,以备不时之需。

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 作为您的核心游戏循环,并通过在主游戏 class 中维护的游戏对象列表上调用 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 的引用或通过从对象调度的事件处理程序来调用它们。