天賦樹這個東西相信大家都不會陌生,比如暗黑破壞神和魔獸世界的天賦樹是灰常經典的(我沒有給暴雪打廣告的意思)。如果你是個做遊戲的,那就有可能會涉及到天賦樹的製作。 先來看一下最終的結果:http://www.iamsevent.com/upload/SpellToolTip.swf 第一步需要解決的問題是技能toolTip的顯示,所謂toolTip,中文翻譯過來叫做貼士,就是一系列解釋性的文字,當鼠標移到一個圖標上時會彈出一個矩形框裏面寫着對此圖標的解釋文字,在Flex中對所有組件都自帶了toolTip的屬性,只要設置了此屬性,鼠標移到相應組件上就會彈出toolTip文字,但是在AS中是沒有的,因此我們就需要自己使用textField類來寫一個toolTip組件:LifeToolTip.as:
1. public class LifeToolTip extends Sprite
2. {
3. public static const NORMAL_VIEW:int = 101;
4. public static const ARROW_VIEW:int = 102;
5.
6. protected var textField:TextField;
7.
8. private var bgW:Number;
9. private var bgH:Number;
10. private var backgroundColor:uint;
11. private var backgroundAlpha:Number;
12. private var type:int;
13. private var Width:Number;
14. private var Height:Number;
15.
16. public function LifeToolTip(text:String="", width:Number=0, height:Number=0, fontColor:uint=0x000000, backgroundColor:uint=0xffffff, backgroundAlpha:Number=0.6, type:int=NORMAL_VIEW )
17. {
18. super();
19. this.mouseEnabled = false;//不允許此組件接收鼠標消息
20. this.backgroundColor = backgroundColor;
21. this.backgroundAlpha = backgroundAlpha;
22. this.type = type;
23. Width = width;
24. Height = height;
25. textField = new TextField();
26. textField.htmlText = text;
27. textField.selectable = false;//不允許文本被選擇
28. textField.textColor = fontColor;
29. textField.multiline = true;//允許文本多行
30. textField.wordWrap = true;//讓文本自動換行
31. addChild( textField );
32.
33. drawToolTipBg();
34. this.visible = false;
35. }
36.
37. //設置文本,重新設置文本後需要根據文字多少重繪背景
38. public function set text( value:String ):void{
39. textField.htmlText = value;
40. drawToolTipBg();
41. }
42.
43. private function drawToolTipBg( ) : void {
44. if(Width == 0){
45. textField.width = textField.textWidth + 5;
46. bgW = textField.width + 10;
47. }else{
48. textField.width = Width;
49. bgW = Width;
50. }
51. if(Height == 0){
52. textField.height = textField.textHeight + 5;
53. bgH = textField.height + 10;
54. }else{
55. textField.height = Height;
56. bgH = Height;
57. }
58. this.graphics.clear();
59. this.graphics.lineStyle(0, 0x000000, .6);
60. if( backgroundAlpha > 1 ){
61. backgroundAlpha = 1;
62. }else if( backgroundAlpha < 0 ){
63. backgroundAlpha = 0;
64. }
65. this.graphics.beginFill( backgroundColor, backgroundAlpha );
66. switch( type )
67. {
68. case NORMAL_VIEW:
69. this.graphics.drawRect(0, 0, bgW, bgH);
70. break;
71. case ARROW_VIEW:
72. this.graphics.lineTo(-10, 20);
73. this.graphics.lineTo(-bgW/2, 20);
74. this.graphics.lineTo(-bgW/2, 20 + bgH);
75. this.graphics.lineTo(bgW/2, 20 + bgH);
76. this.graphics.lineTo(bgW/2, 20);
77. this.graphics.lineTo(10, 20);
78. this.graphics.lineTo(0, 0);
79. textField.x = -bgW/2 + 3; //這裏必須改變一下文本的起始位置於箭頭下矩形框的左側
80. textField.y = 23;
81. break;
82. }
83. this.graphics.endFill();
84. }
85. }
複製代碼這個類裏面難點不是很多,語句都能看明白,沒有陌生的類出現,在構造函數裏我們可以對toolTip組件進行全面的設置,包括顯示文字,寬,高,字體顏色,背景顏色及透明度,還有一個參數是設置外形類別的,外形類別分兩種,第一是最一般的單純的矩形框,第二是帶箭頭的對話框,在drawToolTipBg()函數中會根據其類別繪製不同的外觀。對於箭頭形的外形繪製原理,用一幅圖就很容易理解了。
<ignore_js_op>
如果知道了我們要畫的圖形是怎麼樣的,各個點的位置座標是什麼,就不難用graphics.lineTo方法畫出一個帶箭頭的toolTip框。
好了,接下來,在看到我們做的這個toolTip組件的樣子之前,我們需要先把每個技能的圖標做出來放到舞台上,但是每個技能都有自己的一些屬性,比如技能名,技能等級以及技能説明等,這樣的話就需要一個SpellView類來表現外觀,一個SpellVO類來記錄屬性,這是我們在案例8中提到過的M-V概念,讓視圖與數據分離的思想。好吧,來看看代碼:
SpellVO.as:
1. public class SpellVO
2. {
3. public var name:String;
4. public var level:int;
5. public var maxLevel:int;
6. public var imgSource:String;
7. public var description:String;
8. }
複製代碼
SpellView.as:
1. public class SpellView extends Sprite
2. {
3. private var _spellVO:SpellVO;
4. private var _bm:Bitmap;
5. private var _toolTipStr:String = "";
6. private var toolTipComponent:LifeToolTip;
7.
8. public function SpellView( )
9. {
10. super();
11. _bm = new Bitmap();
12. addChild( _bm );
13. }
14.
15. public function set spellVO( value:SpellVO ):void{
16. //第一次設置時會加載圖片,圖片路徑更改時也會加載
17. if( _spellVO == null || _spellVO.imgSource != value.imgSource ){
18. updateView( value.imgSource );
19. }
20. _spellVO = value;
21. }
22.
23. public function get spellVO():SpellVO{
24. return _spellVO;
25. }
26.
27. private function updateView( url:String ):void{
28. var loader:Loader = new Loader();
29. loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
30. loader.load( new URLRequest( url ) );
31. }
32.
33. private function onComplete(event:Event):void{
34. _bm.bitmapData = ( (event.currentTarget as LoaderInfo).content as Bitmap ).bitmapData ;
35. }
36.
37. }
複製代碼
這次,我們在SpellView裏不直接使用一個public var spellVO:SpellVO而改用get/set方法來進行vo屬性的讀取,因為我們想在spellVO被設置的時候會在後台自動進行一系列的動作。比如,我們注意到在SpellVO中存在一個imgSource的屬性是用來記錄該技能圖標圖片路徑的,我們希望在spellView中的spellVO實例被設置時它會自動加載vo中imgSource記錄的圖片,那麼這種自動化的最佳實現者非set方法莫屬了。 嗯,準備工作都做好了,接下來我們想設置3個技能放到舞台上,那就得給每個技能設置對應的屬性,你可以選擇在主應用文件中一個個設置,這樣比較繁瑣而且不便於修改,修改一次屬性就得重新編譯一次。因此我們選擇了更佳的解決方案——用xml文件。(之前沒有學過XML相關知識的愛卿可以先去網上搜索學習一下)看看我的xml文件裏都記錄了些啥子東東:spellsInfo.xml:
1. <?xml version="1.0" encoding="utf-8"?>
2. <root>
3. <item name="失明學" imgSource="assets/Spell_Arcane_Blink.jpg" maxLevel="3" description="敵方攻擊時有一定機率丟失"/>
4. <item name="流星雨" imgSource="assets/Spell_Arcane_StarFire.jpg" maxLevel="3" description="對範圍內敵人造成傷害"/>
5. <item name="火球術" imgSource="assets/Spell_Fire_FlameBolt.jpg" maxLevel="2" description="對單個敵人造成高額火焰傷害"/>
6. </root>
複製代碼
這樣就設定好了所有需要的技能屬性,我們將在主應用中讀取它,來看主應用代碼,不過貼了代碼後我發現字數超了,看來得貼在二樓了……
才晚了一天就有兩樓被佔領了,貧道只能站在二位腳下繼續發代碼了,獻上主應用的代碼一坨:ToolTipTest:
1. public class ToolTipTest extends Sprite
2. {
3. private var xml:XML;
4. private var spellList:Array = new Array();
5. private var voList:Array = new Array();
6. private static const VIEW_WIDTH:Number = 64;
7. private static const VIEW_HEIGHT:Number = 64;
8. private var _toolTipStr:String = "";
9. private var toolTipComponent:LifeToolTip;
10.
11. public function ToolTipTest()
12. {
13. initXML();
14. }
15.
16. private function initXML():void{
17. var loader:URLLoader = new URLLoader();
18. loader.addEventListener(Event.COMPLETE, onComplete);
19. loader.load( new URLRequest("data/spellsInfo.xml") );
20. }
21.
22. private function onComplete(event:Event):void{
23. xml = new XML( (event.currentTarget as URLLoader).data );
24. for each( var x:XML in xml.item )
25. {
26. var vo:SpellVO = new SpellVO();
27. vo.name = String(x.@name);
28. vo.level = 0;
29. vo.maxLevel = x.@maxLevel;
30. vo.description = String(x.@description);
31. vo.imgSource = String(x.@imgSource);
32. voList.push( vo );
33. }
34. initView();
35. }
36.
37. private function initView():void{
38. var len:int = voList.length;
39. for ( var i:int=0; i<len ; i++ )
40. {
41. var item:SpellVO = voList[i];
42. var view:SpellView = new SpellView();
43. view.spellVO = item;
44. spellList.push( view );
45. view.x = 50 + i * (VIEW_WIDTH + 30);
46. view.y = 50;
47. addChild( view );
48. view.addEventListener(MouseEvent.ROLL_OVER, onMouseOver);
49. view.addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
50. }
51. toolTipComponent = new LifeToolTip("", 100, 0, 0x000000, 0xffffff, 0.6, LifeToolTip.ARROW_VIEW );//為了節省資源,我們應用中只有一個LifeToolTip組件,當鼠標移動到圖標上就讓此組件可見並使其位置移到鼠標上,從圖標上移開時就設置此組件不可見。這裏為了看起來比較酷,我使用了提示框的箭頭形外觀
52. addChild( toolTipComponent );//要保證提示框不被遮擋,就必須最後AddChild
53. }
54.
55. private function onMouseOver(event:MouseEvent):void{
56. addEventListener(Event.ENTER_FRAME, mouseFollow);
57. var vo:SpellVO = (event.currentTarget as SpellView).spellVO;
58. toolTipComponent.text = setToolTip( vo );//設置提示框中顯示信息格式
59. toolTipComponent.visible = true;
60. }
61.
62. private function onMouseOut(event:MouseEvent):void{
63. toolTipComponent.visible = false;
64. removeEventListener(Event.ENTER_FRAME, mouseFollow);
65. }
66.
67. private function mouseFollow(event:Event):void{
68. toolTipComponent.x = mouseX - 1;
69. toolTipComponent.y = mouseY + 5;;// -1和+5是偏移量,為了讓鼠標指針不擋住提示框的箭頭
70. }
71.
72. private function setToolTip( itemVO:SpellVO ):String{
73. //為了實現提示框中不同屬性顯示不同顏色與大小,我們使用了html文本
74. var str:String = toHTMLText(itemVO.name, 20) + "<br>"
75. + toHTMLText("等級:" + itemVO.level + "/" + itemVO.maxLevel, 14, "#666600") + "<br><br>";
76. if(itemVO.level != 0){
77. str += toHTMLText("當前等級", 20, "#ffffff") + "<br>"
78. + toHTMLText(itemVO.description, 14, "#666600") + "<br><br>";
79. }else{
80. str += toHTMLText(itemVO.description, 14, "#666600") + "<br><br>";
81. }
82. return str;
83. }
84.
85. private function toHTMLText(text:String, fontSize:int = 12, color:String = "#000000" ):String{
86. var str:String = "<font size='" + fontSize + "' color='" + color + "'>" + text + "</font>";
87. return str;
88. }
89. }
複製代碼首先要做的自然是從xml中讀取配置信息,在一般的商業項目中,xml文件被稱作配置表,所有遊戲用到的數據都會記錄在其中,他不是寫死在程序中的,所以你可以在外部動態改變遊戲信息。就是説你改變xml中信息後運行swf文件,就會發現遊戲中你所修改的信息生效了。配置表一般是由策劃人員來進行修改的,因為他們的工作就是保持遊戲的平衡性與遊戲性,而這些東西都不是我們程序員需要操心的,把遊戲的信息放在一個可讀性更高且修改方便的xml文件中供一個不懂編程的策劃人員來修改是不會具有任何難以理解的東東的。在把xml中數據遍歷並保存為一個vo數組後我們需要的遊戲數據就得到了,之後把這些數據放入他們所對應的視圖中去。我們還需要給每個視圖加上鼠標事件,為了讓技能的提示框跟隨者鼠標移動,我們一開始的設想是使用MOUSE_OVER事件,因為此事件的官方解釋是鼠標在顯示對象上移動鼠標即會不斷觸發。但是實際效果不佳,由於此事件的觸發並不連貫,鼠標在剛移上技能圖標時會出現技能提示框,但是在出現之後仍操縱鼠標在技能圖標上來回移動,會發現提示框並不能夠一直跟隨鼠標移動,而是間斷性地跟隨。於是我們就不得不把ROLL_OVER與ENTER_FRAME事件聯合起來使用以達到我們想要的效果,ROLL_OVER事件只在鼠標剛移到顯示對象時觸發,當觸發後就開始偵聽ENTER_FRAME事件以保證提示框能夠持續連貫地跟隨鼠標移動……
好吧,如果邏輯有點混亂就把代碼拷到Flash Builder下,然後使用Ctrl + 鼠標左鍵 點擊你不知道有什麼作用的函數,看看這個函數的代碼是怎樣的吧。讓我們來看看主應用的運行效果圖:
<ignore_js_op>
shit!截圖中我的鼠標怎麼不見了!算了,不管他,雖然鼠標不見了但是它永遠留在我們心中……
技能提示框有了,接下來需要做的就是天賦樹了,技能的學習總不可能是隨意的,需要滿足一些前提條件才行,首先,學習一個技能需要消耗技能點。其次,在天賦樹裏面我們不可能一開始就學到很高級的技能,想學習一個高級技能必須具備前提條件,比如需要先學習一些低級技能才可以。還有,每個技能會有很多級,比如一個二級技能必須學習了一級技能後才可以升級,而且在N多的技能中還存在分類,比如一些是冰魔法一些是火魔法什麼的……好了,暫時就想到這一些限制要素,讓我們來用代碼做出這條規則。
首先,我們的技能數據需要增加一些屬性了,為SpellVO中增加id,type,requirePoint以及precondition四個屬性:
1. public class SpellVO
2. {
3. public var id:Number;//每個技能特有的ID標誌號,它記錄了技能的類型以及級別
4. public var type:int;//技能類別
5. public var name:String;
6. public var level:int;
7. public var maxLevel:int;
8. public var requirePoint:int;//學習需要技能點
9. public var precondition:Array;//修煉前提
10. public var imgSource:String;
11. public var description:String;
12. }
複製代碼
接着xml中也必須做出對應的改變:
1. <?xml version="1.0" encoding="utf-8"?>
2. <root>
3. <!--id:第一位是標誌位無意義,前兩位塔的類別,後兩位塔的級別-->
4.
5. <item id="10000" name="失明學" imgSource="assets/Spell_Arcane_Blink.jpg" requirePoint="1" maxLevel="3" precondition="" description="敵方攻擊時有一定機率丟失"/>
6. <item id="10001" name="失明lv1" imgSource="assets/Spell_Arcane_Blink.jpg" requirePoint="1" maxLevel="3" precondition="" description="敵方攻擊時有10%機率丟失"/>
7. <item id="10002" name="失明lv2" imgSource="assets/Spell_Arcane_Blink.jpg" requirePoint="1" maxLevel="3" precondition="" description="敵方攻擊時有20%機率丟失"/>
8. <item id="10003" name="失明lv3" imgSource="assets/Spell_Arcane_Blink.jpg" requirePoint="1" maxLevel="3" precondition="" description="敵方攻擊時有30%機率丟失"/>
9.
10. <item id="10100" name="流星雨" imgSource="assets/Spell_Arcane_StarFire.jpg" requirePoint="1" maxLevel="3" precondition="" description="對範圍內敵人造成傷害"/>
11. <item id="10101" name="流星雨lv1" imgSource="assets/Spell_Arcane_StarFire.jpg" requirePoint="1" maxLevel="3" precondition="" description="對範圍內敵人造成每波流星雨100的傷害"/>
12. <item id="10102" name="流星雨lv2" imgSource="assets/Spell_Arcane_StarFire.jpg" requirePoint="1" maxLevel="3" precondition="" description="對範圍內敵人造成每波流星雨200的傷害"/>
13. <item id="10103" name="流星雨lv3" imgSource="assets/Spell_Arcane_StarFire.jpg" requirePoint="1" maxLevel="3" precondition="" description="對範圍內敵人造成每波流星雨300的傷害"/>
14.
15. <item id="10200" name="火球術" imgSource="assets/Spell_Fire_FlameBolt.jpg" requirePoint="1" maxLevel="2" precondition="10001_10101" description="對單個敵人造成高額火焰傷害"/>
16. <item id="10201" name="火球lv1" imgSource="assets/Spell_Fire_FlameBolt.jpg" requirePoint="1" maxLevel="2" precondition="10002_10102" description="對單個敵人造成500點火焰傷害"/>
17. <item id="10202" name="火球lv1" imgSource="assets/Spell_Fire_FlameBolt.jpg" requirePoint="1" maxLevel="2" precondition="10003_10103" description="對單個敵人造成1000點火焰傷害"/>
18.
19. </root>
複製代碼
在設置時要注意邏輯嚴密,比如你一個技能明明寫的是最高等級為2級(maxLevel=“2”),但你的xml標籤卻只寫到Lv1為止,就不行了嘛……字數有限,樓下繼續連載ing……
説實話,我也不願意一篇文章拖拉拉寫那麼長,但是沒辦法,想跟大家分享的東西不是幾句代碼就能實現的,若真用“一行流”寫完了代碼,各位看了也是雲裏霧裏,這就是在裝B而不是在分享知識了。現在的flash AS相關書籍已經是層出不窮,在論壇上寫的教程都是我個人做過,培訓班裏MoonSpirit教過的感覺比較實用的東西分享給大家,若與某書上的一些案例不謀而合那麼純屬巧合,我也不為了圖什麼利益,純屬個人興趣而已,我也不怕兄弟們學去之後跟我們搶飯碗,現在市場還是供不應求的,大家好才是真的好嘛,哈哈~ OK,言歸正傳了列位仙家。剛才樓上的樓上説到了我們需加載的xml得做一些改變(用“樓上的樓上”一次好像有點……),我們為每一個技能都加上了一個id位,這個id不是隨便設的,它的5位數字中,第一位為標記位,沒有意義,接下來的兩位代碼技能類別,最後兩位代表技能等級。接下來在主應用文件中xml讀取完畢的事件處理函數onComplete中作出相應改變:
1. private function onComplete(event:Event):void{
2. xml = new XML( (event.currentTarget as URLLoader).data );
3. for each( var x:XML in xml.item )
4. {
5. var vo:SpellVO = new SpellVO();
6. vo.id = Number(x.@id);
7. vo.type = vo.id / 100 % 100;
8. vo.name = String(x.@name);
9. vo.level = vo.id % 100;
10. vo.maxLevel = x.@maxLevel;
11. vo.requirePoint = int(x.@requirePoint);
12. vo.description = String(x.@description);
13. var tmpStr:String = x.@precondition;
14. if(tmpStr){
15. vo.precondition = tmpStr.split("_"); //切割字符
16. }else{
17. vo.precondition = [];
18. }
19. vo.imgSource = String(x.@imgSource);
20. voList.push( vo );
21. }
22. initView();
23. }
複製代碼
由於在xml中不可能設置一個數組,我們就使用了一種以字符串來表現數組結構的“偽數組法”,即把多個元素id之間以一個符號隔開,常用的符號有下劃線“_”,豎線“|”等等,在flash中讀取進這個偽數組字符串後必須對其進行切分後才能得到正確的數組,詳細方法還是自己從上面的代碼中領悟吧。恩哼,數據改變了視圖自然也需要做一些大便(本來想打“改變”的,既然打錯了就打錯算了),當技能前提條件沒有滿足處於不可學習狀態時我們需要讓其變成灰色。於是在spellView中我們加上一個設置圖標可選與否的狀態切換接口:
1. private var _canbeUpdated:Boolean = true;
2. public function set canBeUpdated( value:Boolean ):void{
3. _canbeUpdated = value;
4. if( _canbeUpdated ){
5. this.filters = [];
6. }else{
7. this.filters = [new ColorMatrixFilter(
8. [1,0,0,0,0,
9. 1,0,0,0,0, //在案例十一中製作技能冷卻動畫時提到過的灰度矩陣
10. 1,0,0,0,0,
11. 0,0,0,1,0
12. ])];
13. }
14. }
15.
16. public function get canbeUpdated():Boolean{
17. return _canbeUpdated;
18. }
複製代碼
之後,我們在主應用文件裏面添加一個記錄當前可用技能點的變量availablePoint ,並在初始化視圖函數initView中要設置一下圖標初始狀態是灰色的不可學習狀態還是彩色的可學習狀態:
1. private var availablePoint:int = 5;
2. private function initView():void{
3. var len:int = voList.length;
4. var spellCount:int = 0; //記錄需要添加到舞台上的技能圖標數
5. for ( var i:int=0; i<len ; i++ )
6. {
7. var item:SpellVO = voList[i];
8. if( item.level == 0 ){ //取出每個技能的0級狀態添加到舞台上
9. var view:SpellView = new SpellView();
10. view.spellVO = item;
11. spellList.push( view );
12. view.x = 50 + spellCount * (VIEW_WIDTH + 30);
13. view.y = 50;
14. addChild( view );
15. view.addEventListener(MouseEvent.ROLL_OVER, onMouseOver);
16. view.addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
17. if(item.precondition.length > 0 || availablePoint < item.requirePoint){ //如技能有學習前提或者可用技能點數沒有滿足技能要求則不可學習
18. view.canBeUpdated = false;
19. }else{
20. view.canBeUpdated = true;
21. }
22. spellCount++;
23. }
24. }
25. ……
26. }
複製代碼
由於當前記錄在xml中的技能標籤有 1*4 + 1*4 + 1*3 = 11個,不過我們把同一個技能的不同等級寫在了不同標籤中而已,比如前四個標籤都是在描述“失明學”這一個技能。我們需要添加到舞台上的技能圖標實際上只有3個。因此在initView方法中創建視圖時我們僅選取每種技能的第一個標籤信息即可。創建後,在舞台上,我們將看到每個技能的0級圖標,這樣就夠了,在升級後這些技能的圖標的信息或圖標外觀將會發生改變。 剩下要做的就是在鼠標停留於技能圖標上時顯示更多的技能相關信息了,看到setToolTip()方法中,需要改造成這樣:
1. private function setToolTip( itemVO:SpellVO ):String{
2. var str:String = toHTMLText(itemVO.name, 20) + "<br>"
3. + toHTMLText("等級:" + itemVO.level + "/" + itemVO.maxLevel, 14, "#666600") + "<br><br>";
4. if(itemVO.level != 0){
5. str += toHTMLText("當前等級", 20, "#ffffff") + "<br>"
6. + toHTMLText(itemVO.description, 14, "#666600") + "<br><br>";
7. }else{
8. str += toHTMLText(itemVO.description, 14, "#666600") + "<br><br>";
9. }
10. if(itemVO.level != itemVO.maxLevel){
11. var nextSpeel:SpellVO = findNextSpell( itemVO );
12. str += toHTMLText("下一等級", 20, "#55aa55" ) + "\n"
13. +toHTMLText(nextSpeel.description, 14, #55aa55") + "\n\n";
14. }
15. if(itemVO.precondition.length > 0){
16. str += toHTMLText("前提天賦", 18, "#ffffff") + "\n";
17. var isFit:Boolean = false;
18. for each(var elem:String in itemVO.precondition){
19. var conditionVO:SpellVO = findVOById(elem);
20. var color:String = "#ff0000";
21. isFit = checkLearn( conditionVO );
22. if( isFit )color = "#00ff00";
23. str += toHTMLText(conditionVO.name, 14, color) + "</font>\n";
24. }
25. }
26. str += toHTMLText("需求技能點:" + itemVO.requirePoint, 14) + "<br>";
27. return str;
28. }
29.
30. /**
31. * 查找傳入參數spellVO所是否已經學習過
32. */
33. private function checkLearn( value:SpellVO ):Boolean{
34. for each(var vo:SpellVO in learnedSpell){
35. if( vo.id == value.id )return true;
36. }
37. return false;
38. }
39.
40. /**
41. * 根據指定ID查找spellVO,若查找不到則返回NULL
42. */
43. private function findVOById( id:String ):SpellVO{
44. for each(var elem:SpellVO in voList){
45. if( elem.id == Number( id ) ){
46. return elem;
47. }
48. }
49. return null;
50. }
51.
52. /**
53. *
54. * 根據傳入spellVO參數查找它下一級別技能的spellVO,若查找不到則返回NULL
55. */
56. private function findNextSpell( value:SpellVO ):SpellVO{
57. var index:int = voList.indexOf( value );
58. //如果數組中存在此技能則試着查找下一級別
59. if( index != -1 ){
60. //為了保證index+1不因超過數組長度而報錯,這裏需要對index進行一下檢查
61. if( index < voList.length - 1 &&
62. voList[index + 1].type == value.type ){
63. return voList[index + 1];
64. }
65. }
66. return null;
67. }
複製代碼好了,做完這一切後的運行結果正如我們想象得那樣,在技能提示框中顯示出了足夠多的信息,為了美觀,我把toolTipComponent的寬增加了50:
<ignore_js_op>
2010-12-6 23:27:03 上傳
下載附件 (27.6 KB)
啊……今天寫了很多了,休息會明天再接着寫吧……
事實上我們在setToolTip函數中已經做了很多事情,在第10-14行裏判斷了當前技能是否已升至最高等級,若沒有升至最高級別則顯示下一級別信息。在15-25行裏判斷當前技能是否存在前提條件技能,若存在,則遍歷該技能的前提條件數組precondition中所有前提技能的id並通過findVOById方法找到該前提技能ID所對應的VO,之後通過一個叫做checkLearn的方法從learnSpell這個記錄了所有已學習技能的數組中檢查該前提技能是否已學習過,若已學習過則在提示框中顯示該前提技能的文字為綠色,表示已滿足條件,若沒有學習過該技能則顯示紅色,表示未滿足條件。 準備工作都做好了,剩下的事情就是設置點擊技能圖標進行技能學習的操作了。先給主應用添加一個顯示當前有多少可用技能點的文本:
1. private var pointText:TextField;
2. private function initView():void{
3. pointText = new TextField();
4. pointText.text = "可用天賦點數:" + availablePoint;
5. pointText.width = pointText.textWidth + 5;
6. pointText.height = pointText.textHeight + 5;
7. addChild(pointText);
8. ……
複製代碼
之後,我們為每個技能添加鼠標點擊事件偵聽:
1. private function initView():void{
2. ……
3. for ( var i:int=0; i<len ; i++ )
4. {
5. var item:SpellVO = voList[i];
6. if( item.level == 0 ){
7. ……
8. addChild( view );
9. view.addEventListener(MouseEvent.ROLL_OVER, onMouseOver);
10. view.addEventListener(MouseEvent.ROLL_OUT, onMouseOut);
11. view.addEventListener(MouseEvent.CLICK, onClick);
12. ……
13. }
14. ……
15. }
16.
17. private function onClick(event:MouseEvent):void{
18. var sv:SpellView = event.currentTarget as SpellView;
19. if( sv.canBeUpdated ){
20. availablePoint -= sv.spellVO.requirePoint;//扣除技能點
21. pointText.text = "可用天賦點數:" + availablePoint;//更新天賦點數文本
22. var nextSpell:SpellVO = findNextSpell( sv.spellVO ); //找到下一技能vo
23. learnedSpell.push( nextSpell );//向已學習數組中追加元素
24. sv.spellVO = nextSpell; //更新當前點擊技能的vo
25. toolTipComponent.text = setToolTip( sv.spellVO );//更新提示框信息
26. updateAllView();
27. }
28. }
29.
30. /**
31. * 更新所有技能視圖狀態
32. *
33. */
34. private function updateAllView():void{
35. for each(var sv:SpellView in spellList){
36. //檢查技能是否已升至最高級
37. if( findNextSpell(sv.spellVO) == null )
38. {
39. //若已升至最高等級則不繼續之後的“前提條件”與“需求技能點”的檢查,使用continue語句繼續檢查spellList數組中的下一項技能
40. sv.canBeUpdated = false;
41. continue;
42. }
43. //查找具有前提條件的技能並在其前提條件滿足並且可用技能點滿足升級需求技能點時給它置於可選狀態
44. if( sv.spellVO.precondition.length > 0 ){
45. var isFit:Boolean = false;
46. for each(var elem:String in sv.spellVO.precondition){
47. var conditionVO:SpellVO = findVOById(elem);
48. isFit = checkLearn( conditionVO );
49. //若條件中有一個不滿足則不繼續後續條件的檢查了,避免isFit標誌被置回true
50. if( !isFit )break;
51. }
52. if( isFit && sv.spellVO.requirePoint <= availablePoint ){
53. sv.canBeUpdated = true;
54. }else{
55. sv.canBeUpdated = false;
56. }
57. }else{
58. //對於不需要前提條件的技能只需檢查需求點數是否滿足條件
59. if( sv.spellVO.requirePoint > availablePoint ){
60. sv.canBeUpdated = false;
61. }else{
62. sv.canBeUpdated = true;
63. }
64. }
65. }
66. }
複製代碼這裏面的邏輯可能會有點繞,其中涉及了對continue和break語句的靈活運用,不過這些都是在所難免的,當限制條件多的時候為了保證邏輯嚴謹無誤,就必須寫一些很“糾結”的算法,當然我的算法肯定還有可以優化的地方,這裏只是拋個磚以引出玉罷了。雖然if-else語句多得令人蛋疼,但實際執行的效果還是不錯的:
<ignore_js_op>
前提條件滿足一個的情況;為了表示得清楚些,我在spellView裏面加了一個顯示當前技能等級的文本。
<ignore_js_op>
前提條件均滿足的情況;
<ignore_js_op>
其中一個技能學到最高級的情況,注意他的技能提示框中不再顯示“下一等級”的信息。
OK,圖片演示完畢,我這裏只是拋磚引玉,我的代碼還是非常不規範,看看主應用文件裏都有將盡300行代碼了,還有待優化,不過這裏只是給出一個思路而已,更多的功能正等待兄弟們的開發呢,比如你可以做得更加美觀一些,技能直接存在條件關係的還可以用線連接起來啥的。
最後奉上全部源碼: <ignore_js_op>
src.rar (14.86 KB, 下載次數: 573)