├── .gitattributes ├── .gitignore ├── GuideManager.as ├── IGuide.as ├── README.md ├── business ├── AndCalc.as ├── ConditionCalc.as ├── ConditionNode.as ├── Guide.as ├── GuidePosition.as ├── Operators.as ├── OrCalc.as └── guidespace.as ├── guide.xml ├── model ├── GuideConfig.as ├── GuideDirectionType.as ├── GuideModel.as └── GuideSpaceInstanceRegister.as └── view ├── GuideArrowView.as └── GuideRectView.as /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /GuideManager.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide 2 | { 3 | import com.qk.core.guide.business.Guide; 4 | import com.qk.core.guide.business.guidespace; 5 | import com.qk.core.guide.model.GuideConfig; 6 | import com.qk.core.guide.model.GuideDirectionType; 7 | import com.qk.core.guide.model.GuideModel; 8 | import com.qk.core.guide.model.GuideSpaceInstanceRegister; 9 | import com.qk.core.guide.view.GuideArrowView; 10 | import com.qk.core.layer.LayerDef; 11 | import com.qk.core.layer.LayerManager; 12 | import com.qk.core.net.CommandID; 13 | import com.qk.core.net.SocketConnection; 14 | import com.qk.core.util.Globals; 15 | 16 | import flash.display.DisplayObject; 17 | import flash.display.DisplayObjectContainer; 18 | import flash.utils.ByteArray; 19 | import flash.utils.clearInterval; 20 | import flash.utils.setInterval; 21 | 22 | import starling.events.EventDispatcher; 23 | 24 | use namespace guidespace; 25 | 26 | /** 27 | * 引导 28 | * @author nieshulong 29 | * 30 | */ 31 | public class GuideManager extends EventDispatcher 32 | { 33 | private static var _instance:GuideManager; 34 | public static function get instance():GuideManager 35 | { 36 | if(_instance == null) 37 | { 38 | _instance = new GuideManager(); 39 | } 40 | return _instance; 41 | } 42 | /** 43 | * 引导持续时间 总共60s 44 | */ 45 | private static const GUIDE_COUNTDOWN_DURATION:uint = 300; 46 | /** 47 | * 引导持续时间 总共60s 48 | */ 49 | private static const GUIDE_DURATION_COUNT:uint = 180; 50 | /** 51 | * 最大引导步骤 52 | */ 53 | private static const MAX_GUIDE_STEP:uint = 32; 54 | /** 55 | *引导逻辑配置 56 | */ 57 | private var _guideConfig:GuideConfig; 58 | private var _curGuide:Guide; 59 | private var _container:DisplayObjectContainer; 60 | /** 61 | *引导技能,武将,装备,挂机等功能 62 | */ 63 | private var _curFuncArrow:GuideArrowView; 64 | /** 65 | *引导一些点击提示的箭头比如任务,对话框,副本选择等。 66 | */ 67 | private var _curTipsArrow:GuideArrowView; 68 | /** 69 | *是否新手引导 70 | */ 71 | private var _isNoviceGuideEnd:Boolean = false; 72 | private var _isFuncGuideRuning:Boolean = false; 73 | private var _totalGuideStep:uint; 74 | private var _intervalId:uint; 75 | private var _tipsGuideModel:GuideModel; 76 | private var _moduleTipsGuideModel:GuideModel; 77 | private var _count:uint = GUIDE_DURATION_COUNT; 78 | private var _cacheGuideId:uint; 79 | 80 | public function GuideManager() 81 | { 82 | } 83 | 84 | public function setup():void 85 | { 86 | SocketConnection.addCmdListener(CommandID.RETURN_BUFF, onSetCacheGuideIndex); 87 | GuideSpaceInstanceRegister.instance.setup(); 88 | _container = LayerManager.instance.getLayer(LayerDef.BOX); 89 | _guideConfig = new GuideConfig(); 90 | _curFuncArrow = new GuideArrowView(); 91 | _curTipsArrow = new GuideArrowView(); 92 | _tipsGuideModel = new GuideModel(); 93 | _moduleTipsGuideModel = new GuideModel(); 94 | _totalGuideStep = _guideConfig.totalStep; 95 | } 96 | 97 | public function cacheGuideIndex(guideId:uint):void 98 | { 99 | var bytes:ByteArray = Globals.helperBytes; 100 | bytes.writeByte(guideId); 101 | SocketConnection.send(CommandID.REQUEST_BUFF, bytes); 102 | } 103 | 104 | private function onSetCacheGuideIndex(byte:ByteArray):void 105 | { 106 | _cacheGuideId = byte.readByte(); 107 | autoStartGuide(); 108 | } 109 | /** 110 | * 111 | * 系统启动时候自动开启新手引导 112 | */ 113 | public function autoStartGuide():void 114 | { 115 | var isGuided:Boolean = false; 116 | for(var guideId:uint = 1; guideId <= _totalGuideStep; guideId++) 117 | { 118 | isGuided = guideId <= _cacheGuideId; 119 | if(isGuided == false) 120 | { 121 | startGuideById(String(guideId)); 122 | break; 123 | } 124 | } 125 | if(isGuided) 126 | { 127 | endNoviceGuide(); 128 | _isNoviceGuideEnd = true; 129 | } 130 | } 131 | 132 | public function hideTipsGuide():void 133 | { 134 | _curTipsArrow.hide(); 135 | } 136 | 137 | public function get isFuncGuideRuning():Boolean 138 | { 139 | return _isFuncGuideRuning; 140 | } 141 | 142 | guidespace function endGuide(isGuideOver:Boolean = false, isCheckTriggerGuide:Boolean = true):void 143 | { 144 | if(_curFuncArrow) 145 | { 146 | _curFuncArrow.hide(); 147 | } 148 | if(_curGuide == null) 149 | { 150 | return; 151 | } 152 | _curGuide.stop(); 153 | cacheGuideIndex(uint(_curGuide.guideId)); 154 | stopCheckAutoEndGuide(); 155 | _isFuncGuideRuning = false; 156 | if(_curGuide.nextGuide == null) 157 | { 158 | _curGuide.dispose(); 159 | _curGuide = null; 160 | } 161 | else if(_curGuide.nextGuide != null && isGuideOver == false) 162 | { 163 | _curGuide = _curGuide.nextGuide; 164 | if(isCheckTriggerGuide) 165 | { 166 | checkTriggerGuide(); 167 | } 168 | } 169 | } 170 | 171 | public function checkTriggerGuide():void 172 | { 173 | if(_curGuide) 174 | { 175 | _curGuide.checkGuide(); 176 | _curGuide.run(); 177 | } 178 | _curFuncArrow.visible = true; 179 | } 180 | 181 | public function showTipsGuide(target:DisplayObject, dir:String = GuideDirectionType.RIGHT, label:String = "", isDragable:Boolean = false):void 182 | { 183 | if(target == null) 184 | { 185 | return; 186 | } 187 | if(_curTipsArrow.target == target) 188 | { 189 | return; 190 | } 191 | hideTipsGuide(); 192 | _tipsGuideModel.target = target; 193 | _tipsGuideModel.dir = dir; 194 | _tipsGuideModel.label = label; 195 | _tipsGuideModel.isDragable = isDragable; 196 | _container.addChild(_curTipsArrow); 197 | _curTipsArrow.moveTo(_tipsGuideModel.target, _tipsGuideModel.dir, _tipsGuideModel.label, false, _tipsGuideModel.isDragable); 198 | } 199 | 200 | guidespace function showFuncGuide(target:DisplayObject, dir:String = GuideDirectionType.RIGHT, label:String = "", isShowMask:Boolean = false, isFinalGuide:Boolean = false, isDragable:Boolean = true):void 201 | { 202 | if(_curGuide && _curGuide.finished && _curFuncArrow.target != target) 203 | { 204 | endGuide(false); 205 | return; 206 | } 207 | if(target == null) 208 | { 209 | return; 210 | } 211 | if(_curFuncArrow.target == target) 212 | { 213 | _curFuncArrow.updateGuideContent(label); 214 | return; 215 | } 216 | _container.addChild(_curFuncArrow); 217 | _curFuncArrow.moveTo(target, dir, label, isShowMask, isDragable); 218 | if(isFinalGuide && _curGuide) 219 | { 220 | _curGuide.finished = true; 221 | } 222 | startCheckAutoEndGuide(); 223 | _isFuncGuideRuning = true; 224 | } 225 | 226 | private function startCheckAutoEndGuide():void 227 | { 228 | stopCheckAutoEndGuide(); 229 | _intervalId = setInterval(onCheckAutoEndGuide, GUIDE_COUNTDOWN_DURATION); 230 | } 231 | 232 | private function stopCheckAutoEndGuide():void 233 | { 234 | _count = GUIDE_DURATION_COUNT; 235 | clearInterval(_intervalId); 236 | } 237 | 238 | protected function onCheckAutoEndGuide():void 239 | { 240 | --_count; 241 | if(_count == 0) 242 | { 243 | endGuide(false); 244 | stopCheckAutoEndGuide(); 245 | } 246 | } 247 | 248 | private function startGuideById(id:String):void 249 | { 250 | _curGuide = createGuide(id); 251 | checkTriggerGuide(); 252 | } 253 | 254 | private function endNoviceGuide():void 255 | { 256 | endGuide(true); 257 | GuideSpaceInstanceRegister.instance.dispose(); 258 | } 259 | 260 | private function createGuide(guideId:String, variables:Object = null, nextGuideId:String = ""):Guide 261 | { 262 | if(guideId == null || guideId == "") 263 | { 264 | return null; 265 | } 266 | var guideXml:XML = _guideConfig.getGuideNodeXml(guideId); 267 | if(guideXml == null) 268 | { 269 | return null; 270 | } 271 | var guide:Guide = new Guide(guideXml, variables); 272 | if(nextGuideId != "") 273 | { 274 | guide.nextGuide = createGuide(nextGuideId, ""); 275 | } 276 | else 277 | { 278 | guide.nextGuide = createGuide(guide.nextGuideId, ""); 279 | } 280 | return guide; 281 | } 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /IGuide.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide 2 | { 3 | /** 4 | * 引导模块接口 5 | * @author nieshulong 6 | * 7 | */ 8 | public interface IGuide 9 | { 10 | function getGuideParams(name:String):* 11 | } 12 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Guide 2 | 页游和手游常见的新手引导的一个设计思路(有限状态机)。根据功能模块注入来获取位置,update 检查状态来执行下一个符合条件的状态。 3 | 以上逻辑主要靠xml来实现,数据驱动。 4 | -------------------------------------------------------------------------------- /business/AndCalc.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | /** 4 | * 5 | * @author nieshulong 6 | * 7 | */ 8 | public class AndCalc 9 | { 10 | private var _andCalcs:Array; 11 | public function AndCalc(condition:String, variables:Object) 12 | { 13 | var clacs:Array = condition.split(Operators.AND); 14 | _andCalcs = new Array(); 15 | for each(var formula:String in clacs) 16 | { 17 | _andCalcs.push(new ConditionCalc(formula, variables)); 18 | } 19 | } 20 | 21 | public function run(value:*):Boolean 22 | { 23 | for each(var calc:ConditionCalc in _andCalcs) 24 | { 25 | if(!calc.calc(value)) 26 | { 27 | return false; 28 | } 29 | } 30 | return true; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /business/ConditionCalc.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | import avmplus.getQualifiedClassName; 4 | 5 | public class ConditionCalc 6 | { 7 | private static const MAX_OPERATOR_LEN:uint = 2; 8 | private var _operator:String = ""; 9 | private var _value:*; 10 | 11 | public function ConditionCalc(condition:String, variables:Object) 12 | { 13 | translateOperator(condition, variables); 14 | } 15 | 16 | public function calc(value:*):Boolean 17 | { 18 | switch (_operator) 19 | { 20 | case Operators.EQUAL: 21 | return value == _value; 22 | case Operators.BIGGER: 23 | return value > _value; 24 | case Operators.SMALLER: 25 | return value < _value; 26 | case Operators.BIGGER_EQUAL: 27 | return value >= _value; 28 | case Operators.SMALL_EQUAL: 29 | return value <= _value; 30 | case Operators.UNEQUAL: 31 | return value != _value; 32 | case "": 33 | return true; 34 | } 35 | return true; 36 | } 37 | 38 | private function translateOperator(str:String, variables:Object):void 39 | { 40 | if (str.length < MAX_OPERATOR_LEN) 41 | { 42 | return; 43 | } 44 | _operator = str.substr(0, MAX_OPERATOR_LEN); 45 | translateValue(str.substr(MAX_OPERATOR_LEN), variables); 46 | } 47 | 48 | private function translateValue(str:String, variables:Object):void 49 | { 50 | for(var key:String in variables) 51 | { 52 | str = str.replace("{" + key + "}", variables[key]); 53 | } 54 | switch(str) 55 | { 56 | case "true": 57 | _value = true; 58 | return; 59 | case "false": 60 | _value = false; 61 | return; 62 | case "null": 63 | _value = null; 64 | return; 65 | case "undefined": 66 | _value = undefined; 67 | return; 68 | default: 69 | _value = str; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /business/ConditionNode.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | import com.qk.core.guide.GuideManager; 4 | import com.qk.core.guide.model.GuideSpaceInstanceRegister; 5 | 6 | import flash.utils.getQualifiedClassName; 7 | 8 | import org.webgame.ds.HashMap; 9 | 10 | use namespace guidespace; 11 | 12 | /** 13 | * 14 | * @author nieshulong 15 | * 16 | */ 17 | public class ConditionNode 18 | { 19 | private var _valueFormual:OrCalc; 20 | private var _valueTarget:Array; 21 | private var _guide:String; 22 | private var _childConditions:Array; 23 | private var _space:String; 24 | private var _final:Boolean; 25 | private var _pos:String; 26 | private var _dir:String; 27 | private var _isFirstNode:Boolean; 28 | private var _isGuideSucess:Boolean; 29 | private var _isShowMask:Boolean = false; 30 | private var _guideParamTarget:Array; 31 | 32 | public function ConditionNode(xml:XML, variable:Object, isFirstNode:Boolean = false) 33 | { 34 | _isFirstNode = isFirstNode; 35 | _valueFormual = new OrCalc(String(xml.@value), variable); 36 | _valueTarget = String(xml.@target).split("."); 37 | _guide = String(xml.@guide); 38 | _guideParamTarget = String(xml.@param).split("."); 39 | _final = int(xml.@final) == 1; 40 | _space = String(xml.@space); 41 | _pos = String(xml.@pos); 42 | _dir = String(xml.@dir); 43 | _isShowMask = uint(xml.@mask) == 1; 44 | _childConditions = new Array(); 45 | for each(var node:XML in xml.condition) 46 | { 47 | _childConditions.push(new ConditionNode(node, variable)); 48 | } 49 | } 50 | 51 | public function tryGuide(module:* = null):Boolean 52 | { 53 | if(_space != "") 54 | { 55 | var inter:* = getModuleInstanceByName(_space); 56 | if(inter != null) 57 | { 58 | module = inter; 59 | } 60 | } 61 | if(module == null) 62 | { 63 | if(_final) 64 | { 65 | GuideManager.instance.endGuide(false); 66 | } 67 | return false; 68 | } 69 | var target:* = getTarget(module, _valueTarget); 70 | var result:Boolean = _valueFormual.run(target); 71 | //如果当前判断成立,则进入引导状态 72 | if(result) 73 | { 74 | var isGuideSccess:Boolean = false; 75 | var isEverGuide:Boolean = false; 76 | for each(var condition:ConditionNode in _childConditions) 77 | { 78 | isEverGuide = true; 79 | //如果某个条件判断成立了,则中断后续判断 80 | if(condition.tryGuide(module)) 81 | { 82 | isGuideSccess = true; 83 | break; 84 | } 85 | } 86 | if(_guide != "") 87 | { 88 | var guide:String = translateGuideValue(module); 89 | GuideManager.instance.showFuncGuide(target, _dir, guide, _isShowMask, _final); 90 | } 91 | else if(_final) 92 | { 93 | GuideManager.instance.endGuide(false); 94 | } 95 | else if(isGuideSccess == false) 96 | { 97 | } 98 | } 99 | return result; 100 | } 101 | 102 | private function translateGuideValue(module:*):String 103 | { 104 | var target:* = getTarget(module, _guideParamTarget); 105 | return _guide.replace("$param", target); 106 | } 107 | 108 | private function getModuleInstanceByName(name:String):* 109 | { 110 | var instanceMap:HashMap = GuideSpaceInstanceRegister.instance.spaceInstanceMap; 111 | for each(var instance:* in instanceMap.getValues()) 112 | { 113 | var type:String = getQualifiedClassName(instance); 114 | type = type.split("::")[1]; 115 | if (type == name) 116 | { 117 | return instance; 118 | } 119 | } 120 | return null; 121 | } 122 | 123 | private function getTarget(module:*, path:Array):* 124 | { 125 | var target:* = module; 126 | for (var i:int = 0; i < path.length; i++) 127 | { 128 | if(path[i] == "") 129 | { 130 | return target; 131 | } 132 | try 133 | { 134 | if(target == null) 135 | { 136 | return null; 137 | } 138 | target = target["getGuideParams"](path[i]); 139 | } 140 | catch (error:Error) 141 | { 142 | try 143 | { 144 | target = target[path[i]]; 145 | } 146 | catch(error:Error) 147 | { 148 | path = path.slice(0, i + 1); 149 | trace("path", path); 150 | } 151 | } 152 | } 153 | return target; 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /business/Guide.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | import com.qk.core.guide.GuideManager; 4 | import com.qk.core.util.Globals; 5 | import com.qk.core.util.IDispose; 6 | 7 | use namespace guidespace; 8 | 9 | /** 10 | * 11 | * @author nieshulong 12 | * 13 | */ 14 | public class Guide implements IDispose 15 | { 16 | private static const GUIDE_ENTER_FRAME:uint = 200; 17 | private var _conditions:Array; 18 | private var _lastTime:uint; 19 | private var _variables:Object; 20 | private var _guideId:String; 21 | private var _nextGuideId:String; 22 | private var _finished:Boolean; 23 | private var _nextGuide:Guide; 24 | 25 | public function Guide(xml:XML, variables:Object = null) 26 | { 27 | _conditions = new Array(); 28 | this.variables = variables; 29 | for each(var module:XML in xml.condition) 30 | { 31 | _conditions.push(new ConditionNode(module, variables, true)); 32 | } 33 | guideId = String(xml.@id); 34 | nextGuideId = String(xml.@next); 35 | } 36 | 37 | public function get nextGuide():Guide 38 | { 39 | return _nextGuide; 40 | } 41 | 42 | public function set nextGuide(value:Guide):void 43 | { 44 | _nextGuide = value; 45 | } 46 | 47 | public function get finished():Boolean 48 | { 49 | return _finished; 50 | } 51 | 52 | public function set finished(value:Boolean):void 53 | { 54 | _finished = value; 55 | } 56 | 57 | public function get nextGuideId():String 58 | { 59 | return _nextGuideId; 60 | } 61 | 62 | public function set nextGuideId(value:String):void 63 | { 64 | _nextGuideId = value; 65 | } 66 | 67 | public function get guideId():String 68 | { 69 | return _guideId; 70 | } 71 | 72 | public function set guideId(value:String):void 73 | { 74 | _guideId = value; 75 | } 76 | 77 | public function get variables():Object 78 | { 79 | return _variables; 80 | } 81 | 82 | public function set variables(value:Object):void 83 | { 84 | _variables = value; 85 | } 86 | 87 | public function run():void 88 | { 89 | Globals.time.addUpdate(update); 90 | } 91 | 92 | public function stop():void 93 | { 94 | Globals.time.removeUpdate(update); 95 | } 96 | 97 | public function update(gap:Number):void 98 | { 99 | _lastTime += gap; 100 | if(_lastTime >= GUIDE_ENTER_FRAME) 101 | { 102 | _lastTime = 0; 103 | checkGuide(); 104 | } 105 | } 106 | 107 | public function checkGuide():void 108 | { 109 | for each(var node:ConditionNode in _conditions) 110 | { 111 | if(node.tryGuide()) 112 | { 113 | return; 114 | } 115 | } 116 | GuideManager.instance.showFuncGuide(null); 117 | } 118 | 119 | public function dispose():void 120 | { 121 | stop(); 122 | _conditions.length = 0; 123 | _nextGuide = null; 124 | _conditions = null; 125 | _variables = null; 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /business/GuidePosition.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | import flash.geom.Point; 4 | /** 5 | * 6 | * @author nieshulong 7 | * 8 | */ 9 | public class GuidePosition 10 | { 11 | public var arrowPos:Point; 12 | public var guidePos:Point; 13 | } 14 | } -------------------------------------------------------------------------------- /business/Operators.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | /** 4 | * 5 | * @author nieshulong 6 | * 7 | */ 8 | public class Operators 9 | { 10 | public static const BIGGER:String = "bb"; 11 | public static const SMALLER:String = "ss"; 12 | public static const BIGGER_EQUAL:String = "b="; 13 | public static const SMALL_EQUAL:String = "s="; 14 | public static const EQUAL:String = "=="; 15 | public static const UNEQUAL:String = "!="; 16 | public static const OR:String = "||"; 17 | public static const AND:String = "&&"; 18 | } 19 | } -------------------------------------------------------------------------------- /business/OrCalc.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | public class OrCalc 4 | { 5 | private var _orCalcs:Array; 6 | public function OrCalc(str:String, variables:Object) 7 | { 8 | var clacs:Array = str.split(Operators.OR); 9 | _orCalcs = new Array(); 10 | for each(var formula:String in clacs) 11 | { 12 | _orCalcs.push(new AndCalc(formula, variables)); 13 | } 14 | } 15 | 16 | public function run(value:*):Boolean 17 | { 18 | for each(var calc:AndCalc in _orCalcs) 19 | { 20 | if(calc.run(value)) 21 | { 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /business/guidespace.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.business 2 | { 3 | public namespace guidespace; 4 | } -------------------------------------------------------------------------------- /guide.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /model/GuideConfig.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.model 2 | { 3 | /** 4 | * 5 | * @author nieshulong 6 | * 7 | */ 8 | public class GuideConfig 9 | { 10 | [Embed(source="../../../Client/config/xml/guide.xml", mimeType="application/octet-stream")] 11 | private var XmlClass:Class; 12 | private var _xml:XML; 13 | public function GuideConfig() 14 | { 15 | _xml = XML(new XmlClass()); 16 | } 17 | 18 | public function get xml():XML 19 | { 20 | return _xml; 21 | } 22 | 23 | public function getGuideNodeXml(guideId:String):XML 24 | { 25 | return _xml.guide.(@id == guideId)[0]; 26 | } 27 | 28 | public function get totalStep():uint 29 | { 30 | return _xml.totalStep[0].@num; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /model/GuideDirectionType.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.model 2 | { 3 | /** 4 | * 5 | * @author nieshulong 6 | * 7 | */ 8 | public class GuideDirectionType 9 | { 10 | public static const LEFT:String = "left"; 11 | public static const RIGHT:String = "right"; 12 | public static const TOP:String = "top"; 13 | public static const BOTTOM:String = "bottom"; 14 | public static const BOTTOM_RIGHT:String = "bottomRight"; 15 | public static const TOP_LEFT:String = "topLeft"; 16 | } 17 | } -------------------------------------------------------------------------------- /model/GuideModel.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.model 2 | { 3 | 4 | import flash.display.DisplayObject; 5 | 6 | public class GuideModel 7 | { 8 | public var target:DisplayObject; 9 | public var dir:String = GuideDirectionType.RIGHT; 10 | public var label:String = ""; 11 | public var isDragable:Boolean = false; 12 | } 13 | } -------------------------------------------------------------------------------- /model/GuideSpaceInstanceRegister.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.model 2 | { 3 | import com.qk.core.map.MapManager; 4 | import com.qk.core.module.ModuleManager; 5 | import com.qk.core.sceneUI.MenuManager; 6 | import com.qk.core.sceneUI.SceneUIManager; 7 | import com.qk.core.sceneUI.view.OpenSkillTipView; 8 | 9 | import flash.utils.getQualifiedClassName; 10 | 11 | import org.webgame.ds.HashMap; 12 | 13 | /** 14 | * 引导需要用到的实例注册(坐标点不需要策划来配,所以这里需要拿到实例来取得target的坐标点) 15 | * @author nieshulong 16 | * 17 | */ 18 | public class GuideSpaceInstanceRegister 19 | { 20 | private static var _instance:GuideSpaceInstanceRegister; 21 | public static function get instance():GuideSpaceInstanceRegister 22 | { 23 | if(_instance == null) 24 | { 25 | _instance = new GuideSpaceInstanceRegister(); 26 | } 27 | return _instance; 28 | } 29 | 30 | private var _spaceInstanceMap:HashMap; 31 | public function GuideSpaceInstanceRegister() 32 | { 33 | } 34 | 35 | public function setup():void 36 | { 37 | _spaceInstanceMap = new HashMap(); 38 | register(MenuManager.instance); 39 | register(MapManager.instance); 40 | register(SceneUIManager.instance); 41 | register(ModuleManager.instance); 42 | register(OpenSkillTipView.instance); 43 | } 44 | 45 | public function dispose():void 46 | { 47 | if(_spaceInstanceMap) 48 | { 49 | _spaceInstanceMap.clear(); 50 | _spaceInstanceMap = null; 51 | } 52 | } 53 | 54 | public function register(instance:*):void 55 | { 56 | var type:String = getQualifiedClassName(instance); 57 | _spaceInstanceMap.add(type, instance); 58 | } 59 | 60 | public function unregister(key:*):void 61 | { 62 | if(key is Class) 63 | { 64 | _spaceInstanceMap.remove(key); 65 | } 66 | else 67 | { 68 | var curType:String = getQualifiedClassName(instance); 69 | for each(var type:String in _spaceInstanceMap) 70 | { 71 | if(type == curType) 72 | { 73 | _spaceInstanceMap.remove(type); 74 | return; 75 | } 76 | } 77 | } 78 | } 79 | 80 | public function get spaceInstanceMap():HashMap 81 | { 82 | return _spaceInstanceMap; 83 | } 84 | 85 | public function set spaceInstanceMap(value:HashMap):void 86 | { 87 | _spaceInstanceMap = value; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /view/GuideArrowView.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.view 2 | { 3 | import com.qk.core.guide.business.GuidePosition; 4 | import com.qk.core.guide.model.GuideDirectionType; 5 | import com.qk.core.layer.LayerDef; 6 | import com.qk.core.layer.LayerManager; 7 | import com.qk.core.manager.StageManager; 8 | import com.qk.core.manager.UIManager; 9 | import com.qk.core.module.AppModule; 10 | import com.qk.core.uic.BaseSprite; 11 | import com.qk.core.util.DisplayUtil; 12 | import com.qk.core.util.Globals; 13 | import com.qk.core.util.TweenHelper; 14 | 15 | import flash.display.DisplayObject; 16 | import flash.display.DisplayObjectContainer; 17 | import flash.display.MovieClip; 18 | import flash.display.Sprite; 19 | import flash.display.Stage; 20 | import flash.geom.Point; 21 | import flash.geom.Rectangle; 22 | import flash.text.TextField; 23 | import flash.text.TextFieldAutoSize; 24 | import flash.text.TextFormat; 25 | 26 | import org.webgame.utils.AlignType; 27 | 28 | /** 29 | * 引导箭头 30 | * @author nieshulong 31 | * 32 | */ 33 | public class GuideArrowView extends BaseSprite 34 | { 35 | private static const CHECK_ARROW_POSITION_DURATION:uint = 500; 36 | private var _target:DisplayObject; 37 | private var _direction:String = ""; 38 | private var _arrow:MovieClip; 39 | private var _guideTxt:TextField; 40 | private var _label:String; 41 | private var _mask:Sprite; 42 | private var _guideBgMc:MovieClip; 43 | private var _guideBgRect:Rectangle; 44 | private var _maskBounds:Rectangle; 45 | private var _isShowMask:Boolean; 46 | private var _totalTime:uint; 47 | private var _directionList:Array; 48 | private var _directionIndex:uint; 49 | private var _targetPoint:GuidePosition; 50 | private var _guidePosition:GuidePosition; 51 | private var _isDragable:Boolean; 52 | private var _offset:Point; 53 | private var _layer:DisplayObjectContainer; 54 | private var _targetModule:AppModule; 55 | private var _topBottonArrow:MovieClip; 56 | private var _leftRightArrow:MovieClip; 57 | 58 | public function GuideArrowView() 59 | { 60 | _offset = new Point(); 61 | _mask = new Sprite(); 62 | _directionList = [GuideDirectionType.TOP, GuideDirectionType.RIGHT, GuideDirectionType.BOTTOM, GuideDirectionType.LEFT]; 63 | this.mouseChildren = false; 64 | this.mouseEnabled = false; 65 | _layer = LayerManager.instance.getLayer(LayerDef.BOX); 66 | _topBottonArrow = UIManager.instance.getMovieClip("baseUI.xdfs.guideArrowMc0"); 67 | _leftRightArrow = UIManager.instance.getMovieClip("baseUI.xdfs.guideArrowMc1"); 68 | _guideTxt = new TextField(); 69 | var textFormat:TextFormat = new TextFormat("Verdana", 12, 0xFFFFFF); 70 | textFormat.align = TextFieldAutoSize.CENTER; 71 | _guideTxt.defaultTextFormat = textFormat; 72 | _guideTxt.wordWrap = false; 73 | _guideTxt.autoSize = TextFieldAutoSize.CENTER; 74 | } 75 | 76 | override public function dispose():void 77 | { 78 | hide(); 79 | _guidePosition = null; 80 | _topBottonArrow = null; 81 | _leftRightArrow = null; 82 | super.dispose(); 83 | } 84 | 85 | public function get target():DisplayObject 86 | { 87 | return _target; 88 | } 89 | 90 | public function updateGuideContent(label:String):void 91 | { 92 | _label = label; 93 | if(_guideTxt.text != _label) 94 | { 95 | _guideTxt.text = _label; 96 | autoLayout(); 97 | } 98 | } 99 | 100 | public function update(gap:Number):void 101 | { 102 | _totalTime += gap; 103 | if(_totalTime >= CHECK_ARROW_POSITION_DURATION) 104 | { 105 | _totalTime = 0; 106 | alignView(); 107 | alignViewByDirection(); 108 | } 109 | } 110 | 111 | public function moveTo(target:DisplayObject, dir:String, label:String, isShowMask:Boolean = false, isDragable:Boolean = false):void 112 | { 113 | StageManager.registResize(alignView); 114 | _direction = dir; 115 | initGuideMc(); 116 | _target = target; 117 | if(target == null) 118 | { 119 | hide(); 120 | return; 121 | } 122 | _label = label; 123 | _target = target; 124 | _guideTxt.text = _label; 125 | autoLayout(); 126 | _isShowMask = isShowMask; 127 | _isDragable = isDragable; 128 | checkArrowTargetModule(); 129 | alignView(false); 130 | if(_isShowMask == false && _isDragable) 131 | { 132 | Globals.time.addUpdate(update); 133 | } 134 | tween1(); 135 | } 136 | 137 | private function tween1():void 138 | { 139 | TweenHelper.killTweensOf(this); 140 | switch(_direction) 141 | { 142 | case "left": 143 | TweenHelper.to(this, .4, {x:-10, onComplete:tween2}); 144 | break; 145 | case "right": 146 | TweenHelper.to(this, .4, {x:10, onComplete:tween2}); 147 | break; 148 | case "top": 149 | TweenHelper.to(this, .4, {y:-10, onComplete:tween2}); 150 | break; 151 | case "bottom": 152 | TweenHelper.to(this, .4, {y:10, onComplete:tween2}); 153 | break; 154 | } 155 | } 156 | 157 | private function tween2():void 158 | { 159 | TweenHelper.to(this, .2, {x:0, y:0, onComplete:tween1}); 160 | } 161 | 162 | public function hide():void 163 | { 164 | if(_arrow) 165 | { 166 | _arrow.x = 0; 167 | _arrow.y = 0; 168 | } 169 | _target = null; 170 | hideMaskForGuide(); 171 | TweenHelper.killTweensOf(this); 172 | DisplayUtil.removeForParent(this); 173 | StageManager.unregistResize(alignView); 174 | Globals.time.removeUpdate(update); 175 | _directionIndex = 0; 176 | _targetModule = null; 177 | } 178 | 179 | private function alignView(isTween:Boolean = true):void 180 | { 181 | if(target == null) 182 | { 183 | return; 184 | } 185 | var parent:DisplayObjectContainer = target.parent; 186 | while(parent && (parent is Stage) == false) 187 | { 188 | parent = parent.parent; 189 | } 190 | if(parent == null || target.alpha == 0 || target.visible == false) 191 | { 192 | hide(); 193 | return; 194 | } 195 | _targetPoint = getTargetPoint(); 196 | checkArrowDepth(); 197 | if(Math.abs(_arrow.x - _targetPoint.arrowPos.x) <= 1 && Math.abs(_arrow.y - _targetPoint.arrowPos.y) <= 1) 198 | { 199 | _arrow.alpha = 1; 200 | return; 201 | } 202 | TweenHelper.killTweensOf(_arrow); 203 | if(isTween) 204 | { 205 | TweenHelper.to(_arrow, 0.3, {x:_targetPoint.arrowPos.x, y:_targetPoint.arrowPos.y, alpha:1}); 206 | } 207 | else 208 | { 209 | _arrow.alpha = 1.0; 210 | _arrow.x = _targetPoint.arrowPos.x; 211 | _arrow.y = _targetPoint.arrowPos.y; 212 | } 213 | if(_isShowMask) 214 | { 215 | drawMaskForGuide(); 216 | } 217 | else 218 | { 219 | hideMaskForGuide(); 220 | } 221 | } 222 | 223 | private function checkArrowDepth():void 224 | { 225 | if(_targetModule && _layer.contains(_targetModule)) 226 | { 227 | var index:uint = _layer.getChildIndex(_targetModule); 228 | _layer.addChildAt(this, index + 1); 229 | } 230 | else if(_targetModule == null) 231 | { 232 | _layer.addChildAt(this, 0); 233 | } 234 | } 235 | 236 | private function checkArrowTargetModule():void 237 | { 238 | var obj:DisplayObject = _target; 239 | while(obj != null && (obj is Stage) == false) 240 | { 241 | if(obj is AppModule) 242 | { 243 | break; 244 | } 245 | obj = obj.parent; 246 | } 247 | _targetModule = obj as AppModule; 248 | } 249 | 250 | private function alignViewByDirection():void 251 | { 252 | if(_targetPoint == null) 253 | { 254 | return; 255 | } 256 | _directionIndex = 0; 257 | while(_targetPoint.arrowPos.x + _arrow.width > StageManager.stageWidth || _targetPoint.arrowPos.x < 0 || 258 | _targetPoint.arrowPos.y + _arrow.height > StageManager.stageHeight || _targetPoint.arrowPos.y < 0) 259 | { 260 | DisplayUtil.removeForParent(_arrow); 261 | _direction = _directionList[_directionIndex]; 262 | initGuideMc(); 263 | _guideTxt.text = _label; 264 | autoLayout(); 265 | alignView(false); 266 | ++_directionIndex; 267 | if(_directionIndex >= _directionList.length) 268 | { 269 | hide(); 270 | break; 271 | } 272 | } 273 | } 274 | 275 | private function initGuideMc():void 276 | { 277 | DisplayUtil.removeForParent(_arrow); 278 | switch(_direction) 279 | { 280 | case "left": 281 | _arrow = _leftRightArrow; 282 | _arrow.rotationMc.scaleX = -1; 283 | break; 284 | case "right": 285 | _arrow = _leftRightArrow; 286 | _arrow.rotationMc.scaleX = 1; 287 | break; 288 | case "top": 289 | _arrow = _topBottonArrow; 290 | _arrow.rotationMc.scaleY = 1; 291 | break; 292 | case "bottom": 293 | _arrow = _topBottonArrow; 294 | _arrow.rotationMc.scaleY = -1; 295 | break; 296 | } 297 | _guideBgMc = _arrow.rotationMc.bgMc; 298 | _arrow.addChild(_guideTxt); 299 | _guideBgRect = _guideBgMc.getBounds(_arrow); 300 | this.addChild(_arrow); 301 | } 302 | 303 | private function adjustPos(data:Object = null):void 304 | { 305 | if(_target != null) 306 | { 307 | _guideBgMc.visible = true; 308 | _guideBgMc.alpha = 0; 309 | var point:GuidePosition = getTargetPoint(); 310 | _guideBgMc.x = point.guidePos.x; 311 | _guideBgMc.y = point.guidePos.y; 312 | TweenHelper.to(_guideBgMc, .5, {alpha:1}); 313 | } 314 | drawMaskForGuide(); 315 | } 316 | 317 | private function drawMaskForGuide():void 318 | { 319 | if(_mask == null) 320 | { 321 | _mask = new Sprite(); 322 | } 323 | createMaskBounds(); 324 | _mask.graphics.clear(); 325 | _mask.graphics.beginFill(0x000000, .6); 326 | _mask.graphics.drawRect(0, 0, StageManager.stage.stageWidth, StageManager.stage.stageHeight); 327 | _mask.graphics.drawEllipse(_maskBounds.x, _maskBounds.y, _maskBounds.width, _maskBounds.height); 328 | _mask.graphics.endFill(); 329 | this.addChildAt(_mask, 0); 330 | } 331 | 332 | private function hideMaskForGuide():void 333 | { 334 | DisplayUtil.removeForParent(_mask); 335 | } 336 | 337 | private function getTargetPoint():GuidePosition 338 | { 339 | var bounds:Rectangle = _target.getBounds(_target); 340 | if(_guidePosition == null) 341 | { 342 | _guidePosition = new GuidePosition(); 343 | } 344 | _offset.x = 0; 345 | _offset.y = 0; 346 | switch(_direction) 347 | { 348 | case "left": 349 | _offset.x = -(_arrow.width + bounds.width) / 2 - 1; 350 | break; 351 | case "right": 352 | _offset.x = (_arrow.width + bounds.width) / 2 + 1; 353 | break; 354 | case "top": 355 | _offset.y = -(_arrow.height + bounds.height) / 2 - 1; 356 | break; 357 | case "bottom": 358 | _offset.y = (_arrow.height + bounds.height) / 2 + 1; 359 | break; 360 | case "topLeft": 361 | _offset.y = -_arrow.height; 362 | _offset.x = -_arrow.width / 2 - 1; 363 | break; 364 | } 365 | var pt:Point = DisplayUtil.alignPoint(_arrow, bounds, AlignType.MIDDLE_CENTER, _offset); 366 | _guidePosition.arrowPos = _target.localToGlobal(pt); 367 | return _guidePosition; 368 | } 369 | 370 | private function createMaskBounds():void 371 | { 372 | var bounds:Rectangle = _target.getBounds(_target); 373 | var topLeft:Point = target.localToGlobal(new Point(bounds.left, bounds.top)); 374 | _maskBounds = bounds.clone(); 375 | _maskBounds.x = topLeft.x; 376 | _maskBounds.y = topLeft.y; 377 | _maskBounds.inflate(3, 3); 378 | } 379 | 380 | private function autoLayout():void 381 | { 382 | var targetWidth:uint; 383 | if(_direction == GuideDirectionType.LEFT || _direction == GuideDirectionType.RIGHT) 384 | { 385 | targetWidth = _guideTxt.width + 40 < 153 ? 153 : _guideTxt.width + 40; 386 | _guideBgMc.width = targetWidth; 387 | _guideBgMc.x = 26; 388 | _guideBgRect = _guideBgMc.getBounds(_arrow); 389 | _arrow.rotationMc.topLeftMc.x = 0; 390 | _arrow.rotationMc.topRightMc.x = _guideBgMc.width + _guideBgMc.x - 6; 391 | _guideTxt.x = ((_guideBgRect.width - _guideTxt.width) >> 1) + _guideBgRect.x; 392 | _guideTxt.y = _guideBgRect.y + (_guideBgRect.height - _guideTxt.height) / 2; 393 | } 394 | else 395 | { 396 | targetWidth = _guideTxt.width + 30 < 172 ? 172 : _guideTxt.width + 30; 397 | _guideBgMc.width = targetWidth; 398 | _guideBgRect = _guideBgMc.getBounds(_arrow); 399 | _arrow.rotationMc.topArrowMc.x = (_guideBgMc.width - _arrow.rotationMc.topArrowMc.width) >> 1; 400 | _arrow.rotationMc.bottomArrowMc.x = (_guideBgMc.width - _arrow.rotationMc.bottomArrowMc.width) >> 1; 401 | _guideTxt.x = (_guideBgRect.width - _guideTxt.width) >> 1; 402 | _guideTxt.y = _guideBgRect.y + (_guideBgRect.height - _guideTxt.height) / 2; 403 | } 404 | } 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /view/GuideRectView.as: -------------------------------------------------------------------------------- 1 | package com.qk.core.guide.view 2 | { 3 | import com.qk.core.manager.StageManager; 4 | import com.qk.core.manager.UIManager; 5 | import com.qk.core.util.DisplayUtil; 6 | import com.qk.core.util.IDispose; 7 | 8 | import flash.display.DisplayObject; 9 | import flash.display.MovieClip; 10 | import flash.display.Sprite; 11 | import flash.geom.Point; 12 | import flash.geom.Rectangle; 13 | 14 | import org.webgame.utils.AlignType; 15 | 16 | /** 17 | * 18 | * @author nieshulong 19 | * 20 | */ 21 | public class GuideRectView extends Sprite implements IDispose 22 | { 23 | private var _guideRect:MovieClip; 24 | private var _target:DisplayObject; 25 | 26 | public function GuideRectView() 27 | { 28 | super(); 29 | this.mouseEnabled = false; 30 | this.mouseChildren = false; 31 | } 32 | 33 | public function dispose():void 34 | { 35 | hide(); 36 | _guideRect = null; 37 | _target = null; 38 | } 39 | 40 | public function moveTo(target:DisplayObject):void 41 | { 42 | if(_guideRect == null) 43 | { 44 | _guideRect = UIManager.instance.getMovieClip("ui.zwj.guide.guideRectMc"); 45 | this.addChild(_guideRect); 46 | } 47 | if(_target == target) 48 | { 49 | return; 50 | } 51 | if(target == null) 52 | { 53 | hide(); 54 | return; 55 | } 56 | StageManager.registResize(alignRectArrow); 57 | DisplayUtil.playAllMovieClip(_guideRect); 58 | _target = target; 59 | _target.parent.addChild(this); 60 | alignRectArrow(); 61 | } 62 | 63 | private function alignRectArrow():void 64 | { 65 | if(_target == null) 66 | { 67 | return; 68 | } 69 | var bounds:Rectangle = _target.getBounds(_target); 70 | _guideRect.width = bounds.width + 1; 71 | _guideRect.height = bounds.height + 1; 72 | var pt:Point = getTargetPoint(_target); 73 | _guideRect.x = pt.x; 74 | _guideRect.y = pt.y; 75 | } 76 | 77 | public function hide():void 78 | { 79 | DisplayUtil.stopAllMovieClip(this); 80 | DisplayUtil.removeForParent(this); 81 | StageManager.unregistResize(alignRectArrow); 82 | _target = null; 83 | } 84 | 85 | private function getTargetPoint(target:DisplayObject):Point 86 | { 87 | var bounds:Rectangle; 88 | bounds = target.getBounds(target.parent); 89 | var pt:Point = DisplayUtil.alignPoint(_guideRect, bounds, AlignType.MIDDLE_LEFT); 90 | return pt; 91 | } 92 | } 93 | } --------------------------------------------------------------------------------