人工智能-基于位置的触发器管理

一切都是偶然,偶然的位置,偶然的角落,偶然的书架,偶然的拿起了一本书,偶然的翻开了偶然的一页,偶然的看了几眼,于是触发器就这么诞生了。

曾经玩过星际争霸1、2,魔兽争霸 地图编辑器的少年们,一定很熟悉触发器的说。玩家可以在地图上放置任意图层,给图层命名,设置图层的大小,然后在触发器中对这些图层设置触发指令。比如玩家移动到下一图层,或者进攻到下一图层,或者该图层内的建筑爆炸,等等。


当然了,触发器不仅仅是图层这一触发功能,还有时间触发器。非常著名的 星际争霸1中的 7vs1 地图就是基于时间触发器的倒计时rpg地图。怪物每个一段的时间触发怪物创建的指令,然后调用图层触发器,往7个玩家所在的图层进攻过去。 玩家的胜利方式可以是 特定图层内没有地方的建筑,又或者是全地图上都没有地方建筑,单位。

本文只谈谈基于位置的触发器实现,时间触发器的话,改日吧!

在AS中有这么一个可以判断位置的API

1
Rectangle.contains(x : int, y : int);

有了这么一个API,就能知道目标是否进入了范围。

首先建立触发器接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.Trigger
{
    import flash.events.IEventDispatcher;
    import flash.geom.Rectangle;

    /**
     * 触发器接口
     */

    public interface ITrigger extends IEventDispatcher
    {
        function get rect() : Rectangle;    // 触发区域
        function get name() : String;       // 触发器名
        function init()     : void;         // 初始化
        function onEnter()      : void;         // 进入触发
        function onExit()       : void;         // 退出触发
    }
}

然后构建一个具体的触发器
这是一个触发前情提要的触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package com.Trigger
{
    import com.Manages.ChatManager;
    import com.Manages.GameStateManager;
    import com.Manages.ModelManager;
    import com.drama.DramaView;
   
    import flash.display.DisplayObjectContainer;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Point;
    import flash.geom.Rectangle;
   
    /**
     * 游戏前情提要剧情
     */

    public class introTrigger implements ITrigger
    {
        public static const NAME : String = "introTrigger";
       
        private var pos         : Point;        //中心点
        private var dis         : int   = 100;  //范围
        private var range           : Rectangle;    //触发圈
        private var storyStage      : Sprite;
        private var root            : DisplayObjectContainer;
        private var disspatcher : EventDispatcher;
       
        public function get rect():Rectangle
        {
            return range;
        }
       
        public function get name():String
        {
            return NAME;
        }
       
       
        /**
         * 初始化建立触发范围
         */

        public function init():void
        {
            disspatcher = new EventDispatcher(this);
           
            pos = new Point(280,380);
            range = new Rectangle(pos.x - dis, pos.y - dis, dis * 2, dis * 2);
            root = GameStateManager.instance.root;
        }
       
        /**
         * 调用剧情模块加载剧情
         */

        public function onEnter():void
        {
            ChatManager.instance.hideChatPanel();
           
            storyStage=new Sprite();
            storyStage.graphics.beginFill(0,0);
            storyStage.graphics.drawRect(0,0,root.stage.stageWidth,root.stage.stageHeight);
            storyStage.graphics.endFill();
            root.addChild(storyStage);
           
            addEventListener("DramaEnd",dramaOver);
           
            DramaView.instance.init(0,"drama01.xml",storyStage,disspatcher);
        }
       
        public function dramaOver(e:Event):void{onExit()};
       
        /**
         * 退出触发器,资源销毁
         */

        public function onExit():void
        {
            DramaView.instance.destory();
            root.removeChild(storyStage);
            ChatManager.instance.showChatPanel();
           
            disspatcher.removeEventListener("DramaEnd",dramaOver);
            disspatcher = null;
        }
       
        public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
        {
            disspatcher.addEventListener(type,listener,useCapture,priority,useWeakReference);
        }
       
        public function dispatchEvent(event:Event):Boolean
        {
            return disspatcher.dispatchEvent(event);
        }
       
        public function hasEventListener(type:String):Boolean
        {
            return disspatcher.hasEventListener(type);
        }
       
        public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
        {
            disspatcher.removeEventListener(type,listener,useCapture);
        }
       
        public function willTrigger(type:String):Boolean
        {
            return disspatcher.willTrigger(type);
        }
       
    }
}

然后我们需要一个触发管理器来管理这些触发器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package com.Manages
{  
    import com.Trigger.ITrigger;
    import com.Trigger.endTrigger;
    import com.Trigger.introTrigger;
    import com.Trigger.trideTrigger;
    import com.client.Player;

    /**
     * 触发器管理
     * 基于位置的触发
     *
     * Create Date:2012-04-15
     */

    public class TriggerManager
    {
        private static var _instance : TriggerManager;
       
        private var triggerArr : Array;
        private var mainPlayer : Player;

        public static function get instance() : TriggerManager
        {
            if(!_instance)_instance = new TriggerManager();
           
            return _instance;
        }
       
        /**
         * 初始化触发器队列
         */

        public function init() : void
        {
            addTrigger(introTrigger);                       //前情提要触发器
        }
       
        /**
         * 添加触发器
         */

        public function addTrigger(target : Class) : void
        {
            if(null == triggerArr)triggerArr = new Array();
           
            var cls : Class = target;
            var content : ITrigger = new cls() as ITrigger;
            content.init();
           
            triggerArr.push(content);
        }
       
        /**
         * 移除触发器
         */

        public function removeTrigger(name : String) : void
        {
            var trigger : ITrigger;
           
            for each(trigger in triggerArr){
                if(trigger.name == name){
                    triggerArr.splice(triggerArr.indexOf(trigger),1);
                }
            }
        }
       
        /**
         * 触发检测
         */

        public function update(tick : Number) : void
        {
            mainPlayer = ModelManager.instance.mainPlayer;
           
            if(null == mainPlayer) return;
           
            var trigger : ITrigger;
           
            for each(trigger in triggerArr){
                if(trigger.rect.contains(mainPlayer.x,mainPlayer.z)){   //触发
                    trigger.onEnter();                                  //进入触发器
                    triggerArr.splice(triggerArr.indexOf(trigger),1);   //从触发队列移除
                }
            }
        }
       
        /**
         * 销毁触发器队列
         */

        public function destory() : void
        {
            var trigger : ITrigger;
           
            while(triggerArr.length>0)
            {
                trigger = triggerArr.shift();
                trigger = null;
            }
           
            triggerArr = null;
        }
    }
}

在初始化函数中,构造需要响应的触发器
在状态机中检测玩家是否达到区域
到达区域后进入触发器,触发具体指令
触发后,将该触发器从触发列表中移除

目前该触发器只针对主角进行触发,如果要制作一款AI,则需要建立一套完善的触发机制,除了位置触发,时间触发外还包括声音触发,范围触发,移动的位置触发,固定的位置触发,以及由此引申的逃避行为,追击行为,防守行为 等等。

其实要做一款类似星际,魔兽的地图编辑器一点也不浮云

未完待续~~

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.