Kiến thức lập trình căn bản ( cho jassuser , nếu bạn dùng GUI, cũng nên xem qua 1 ít Bạn muốn hỏi các bro trên forum , muốn nhờ fix dùm bug, hoặc đơn giản hơn , bạn muốn làm quen với lập trình jass ? Tất cả những điều bạn muốn ở trên, đạt được hay không, thường phụ thuộc vào 1 thứ mà bạn ít nghĩ đến nhất . Đó là sự rõ ràng trong lập trình. Tại sao nó lại quan trọng như vậy ? Nếu bạn không tuân thủ việc lập trình minh bạch, hậu quả bạn nhận được sẽ là số bug vô bờ bến , số memory leak thừa mứa , và spell của bạn, nếu phải sửa , thì sẽ mất thời gian gấp nhiều lần bình thường. Bài viết này mục tiêu mang lại cho bạn 1 phong cách nhất quán, minh bạch trong lập trình jass cơ bản - giúp bạn làm map khoa học hơn. Các bro nếu có freetime, sẵn sàng giúp bạn fix bug, test map thôi. Nhưng các bro mà nhìn vào mớ code tùm lum tè le ko trật tự ko quy cách ko tiêu chuẩn gì của bạn, ko chắc họ sẽ tiếp tục, hay lại fang 1 câu : "tớ bận quá, để sau nhé". Đối với những bạn mới làm quen với lập trình , thường mắc những lỗi căn bản sau : 0) Lập quá nhiều trigger , gây rối mắt. 1) Code liền 1 mạch ko xuống dòng. Lỗi này làm mớ code của bạn trở thành 1 cái thùng rác.mà chẳng ai có hứng thú lục thùng rác dùm bạn cả. 2) Code liền 1 mạch ko ghi chú.Lỗi này làm mớ code của bạn dù đơn giản nhưng trông phức tạp hơn khoảng 70%. Người đọc phải đọc thật kỹ, vì biến của bạn đặt ra thật vô phương hiểu được vì nó là những từ được lai giống giữa 3 cái sau : tiếng anh+tiếng việt+viết tắt. Tôi bỏ làm map mấy tháng rồi, giờ giở lại code của chính mình còn ko hiểu 1 số chỗ, nên phải ngồi bỏ 1 buổi tối ghi chú lại cho tất cả các hàm cái library ( cái library này tự tôi tạo ). 3)Xóa 1 biến đi mất tiêu rồi còn dùng , hoặc chưa khai báo mà đã dùng , hoặc ko thèm xóa biến sau khi dùng. Lỗi này là lỗi dẫn tới map bạn bị memory leak ( yên tâm bác nào chơi thanh ram 16 GB thì khỏi lo vụ này ). Hãy tập cho mình thói quen lập trình thật rõ ràng , sau đây là giải pháp : Giả pháp 0) Lập quá nhiều trigger, gây rối mắt. Giải pháp : cho tất cả trigger của bạn vào một library. Phân loại ra library spell, library system .v.v.. Cú pháp : Mã: library MasterHandy //================================================ // masterco's library //================================================ //Dung de bat 1 unit stop function stop takes unit u returns nothing call IssueImmediateOrder( u, "stop" ) endfunction //================================================ endlibrary Trong đó : MasterHandy là tên library , 3 dòng tiếp theo là chú thích cho dễ nhìn, bên trong là các hàm (function ) của bạn, ví dụ là bắt 1 unit stop, bạn phải type : call IssueImmediateOrder( u, "stop" ) , thì nay, với hàm stop này, bạn chỉ cần ghi stop(u) thì nó sẽ stop. Giữa các hàm , bạn phải có ghi chú công dụng của hàm theo cách dễ hiểu nhất, và phải có đường biên phân cách như //================================================ Thông tin ngoài lề :Trước khi 1 unit bắt đầu làm 1 animation nào đó , thì bạn nên bắt nó stop trước, thì hành động của nó mới chính xác thời gian. Nhờ lập library, sau này bạn rất được lợi về thời gian làm spell. Ví dụ : spell meteor strike cần làm 1 quả meteor bay dần dần xuống và lăn đi 1 đoạn, vừa lăn vừa gây dam / Spell Spear di chuyển nhân vật cầm cây giáo chọc thẳng tới phía trước và gây dam ...v..v... 2 spell này đều có điểm chung là cần di chuyển 1 vật đi 1 distance, trong 1 time , và gây dam trên đường đi. Bạn chỉ cần tạo 1 hàm dash( u , ang ,dis, time ) Trong đó : u : unit cần di chuyển / ang : góc mà nó sẽ di chuyển / dis : khoảng cách mà nó sẽ di chuyển / time : thời gian nó đến đích là bao lâu. Sau đó bạn tạo 1 hàm dmgarea( u , dmg , loc ,rad ) Trong đó : u : unit gây dmg / dmg : số dmg / loc : vị trí mà nó gây dmg / rad : diện tích mà nó gây dmg. Có 2 hàm này, sau này làm spell bạn ko cần lập trình lại nữa, mà chỉ việc gọi hàm dash , và dmgarea , và đưa đủ thông số yêu cầu là ok. Giải pháp 1và 2 ) Code liền 1 mạch ko xuống dòng, ko ghi chú . Trước khi nêu giải pháp, bạn hãy nhìn function dưới đây : ---------- Post added at 16:27 ---------- Previous post was at 16:26 ---------- Hàm được chia ra làm 3 vùng : vùng khởi tạo để khai báo các biến, vùng xử lý , và cuối cùng là vùng hủy biến. Nếu bạn thích rõ ràng hơn , thì phân cách mỗi vùng bằng //-------------câu chú thích--------------------- Nhìn vào hàm, ta thấy ngay 3 vùng , và nếu bắt tay sửa lỗi sẽ rất dễ dàng. Ngòai việc xuống dòng, bạn còn phải vô lề như code dưới, if nằm trong loop. Nếu như nhiều if nằm trùng nhau mà ko vào lề, thì khi bạn sửa sẽ chẳng biết code nào thuộc if nào. loop exitwhen( i > gnum-1) if( i != chose ) then set u = null endif set i = i+1 endloop Thông tin ngoài lề : Khi khai báo biến , nên thống nhất cho tất cả các biến theo 1 quy cách nào đó, để dù nhìn vào code nào, ta cũng nhận ra công dụng của nó ngay. Theo kinh nghiệm của tôi, khi bạn tuân thủ theo quy cách khai báo biến , thì bạn ko cần nhiều chú thích mà vẫn hiểu rõ ràng code. Mã: Ví dụ : unit chính : u unit dummy : dum unit trong group : e effect : eff string : s group : g timer : t location : loc Nếu có 2 dum thì dum1 , dum2. Còn về tên hàm : nên thống nhất 1 kiểu đặt tên : ví dụ cho hàm tạo 1 unit : Mã: type1 : createunit type2 : create_unit type3 : Create_Unit type4 : create_u Trong đó xét về độ khoa học , thì type4 là ngắn gọn và khoa học nhất . Vì nó có khoảng cách ,và còn tuân thủ theo cách đặt tên biến ở trên kia. ( nhưng thú thật, tôi đang dùng type1 ) ---------- Post added at 16:29 ---------- Previous post was at 16:27 ---------- Giải pháp 3)Xóa 1 biến đi mất tiêu rồi còn dùng , hoặc chưa khai báo mà đã dùng , hoặc ko thèm xóa biến sau khi dùng Điển hình như sau : Mã: call DestroyEffect(eff) set eff =null Tại sao Destroy rồi còn phải set null ? Khi destroy effect vụ nổ, bạn chỉ hủy cái hình ảnh vụ nổ trong bộ nhớ, còn cái biến eff để trỏ tới nó thì vẫn còn đó, dù ko dùng nữa , nhưng vùng nhớ đó ko được giải phóng để sử dụng. Nếu bạn làm spell có tầm 300 cái effect trên giây, thì sẽ đứng máy sớm thôi. Để biết khi nào thì spell có memory =leak, bạn cứ tạo 1 bầy đàn unit có spell đó ( tầm 25 con ), sau đó tạo 1 trigger có event là khi player bấm ESC , trong trigger đó là group 25 thằng cu đó lại, sau đó bắt nó cast spell ( phải đúng order string trong spell đó nó mới cast nhé ). Nên để spell manacost là 0 , cooldown là 0 ,và loại spell là Unit để tránh rắc rối. 1 spell MUI đầy effect 25 unit cùng cast mà ko giật máy , nếu bạn ko tin cứ pm tôi sẽ send 1 example cho . Map có 10 người chơi mà dùng ultimate liên tục cứ gọi là thoải mái đi, nếu bạn master cái vụ này . Xem code mẫu : Mã: local unit u set u = createunit(....) call dash(u...) set u = null Trong đó , hàm dash là dash liên tục trong 1 thời gian, ví dụ 1 giây đi. Thì khi mình set u = null mất rồi, thì nó có dash tiếp ko ? Câu trả lời là có ! Vì unit u là biến local, nó chỉ là trong khu vực hàm đó thôi, khi bạn gọi dash(u...) thì u được bay tới hàm dash rồi, và cuối hàm này bạn có set nó null chăng nữa, chỉ là giải phóng cái biến local này thôi, unit u đã được trỏ tới hàm dash rồi. Vậy nếu bạn Removeunit u , thì nó còn dash ko ? Câu trả lời là không! Vì hàm remove ( với unit, destroy với effect... ) thì nó ko phải giải phóng vùng nhớ, mà là hủy cái con unit của bạn đi! Để khắc phục những memory leak hiệu quả nhất, đầu tiên bạn nên set null tất cả các biến ở khu vực cuối hàm. Yên tâm set null ở hàm này , nhưng nó vẫn chạy ổn ở hàm khác, vì biến ta dùng là biến local. Tiếp theo : bạn nên tạo 2 hàm sau trong library : destroyunit( u, time) : sau 1 khoảng thời gian thì Remove unit u, và set u = null destroyeffect(eff,time) : sau 1 khoảng thời gian thì Destroy effect eff và set eff = null Ví dụ trong 1 spell bạn tạo 1 dummy có mang 1 quả cầu lửa trên đầu, spell có animation trong 2 giây. thì bạn : Mã: local unit dum = createunit(.....) // tạo unit dummy local effect eff = createeff(dum...) //gắn effect lên người nó call destroyunit(dum,2) // destroy và set null dummy sau 2 giây call destroyeffect(eff,time) // destroy và set null effect sau 2 giây set dum = null set eff = null Chỉ có thế thôi, là bạn hoàn toàn yên tâm sau 2 giây , effect và unit dummy sẽ bốc hơi như chưa bao giờ hiện hữu , ko để lại 1 memory leak nào, mà bạn ko cần phải đi lần mò remove với cả set null nó nữa. Còn location thì thường là dùng xong hủy ngay, nên ko cần tạo hàm time. Còn gì nữa nhỉ, thôi hôm nay thế thôi, có gì tôi edit sau. Have fun programming ![/B]
Em thêm đoạn này vào giữa chỗ "xem function dưới đây" và chỗ "hàm được chia làm 3 vùng" , anh merge dùm em. Một số chỗ sai chính tả, hoặc dư dấu = ở chỗ "memory =leak" nữa, hix , muốn edit mà ko đc. Còn vụ BJ em nghĩ là tốt chứ , lập trình nhanh hơn , ... nhưng 1 số cái bắt buộc phải làm BJ như hàm dash, hàm jump... Mã: function Tên takes ... returns ... Khai báo biến Xử lý biến Hủy biến Return ( nếu có ) endfunction
forum giới hạn số ký tự cho 1 post ít quá, giờ cái post trên cùng sai 1 số chỗ mà tui ko thể sửa đc, cứ sửa là nó báo lỗi ko cho save , trời ơiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii!!!!
Bài dài quá thì chia làm 2 bài rồi nhờ Tom edit lại. Nói chung là bài viết hay và rõ ràng, thích hợp cho người mới học Jass.
Mã: function stop takes unit u returns nothing call IssueImmediateOrder( u, "stop" ) endfunction cái này không khác gì BJ, không nên dùng. -- hàm destroyunit cũng không cần thiết, có UnitApplyTimedLife rồi, nếu unit có Death Type là: Can't Raise, Does Not Decay thì không khác gì remove. hàm destroyeffect thì phải xem xét nội dung ra sao. ngoài ra: cách đặt tên hàm thế này là không nên, đặt tên viết hoa chữ đầu, như: DestroyEfffectTimed, như vậy sẽ dễ nhìn hơn. Hoặc dùng dấu gạch dưới: destroy_effect --- còn vụ viết lại thì phải xem hàm viết lại có hữu dụng không, nếu viết lại mà cũng chỉ gọi một native như BJ thì đừng viết, phí công. --- p.s: anh đã nói trên PM rồi, cứ post thêm bài thứ 2, 3 đi, anh sẽ merge vào post thứ 2, 3 cho. ==== không merge được rồi , tạm merge lên post #2, mọi người chịu khó đọc thế này nhé
Mã: function grouprandomunit takes group g returns unit local unit array[] u local unit e = null local integer gnum = 0 local integer chose local integer i = 0 loop set e = FirstOfGroup(g) exitwhen e == null set u[gnum] = e call GroupRemoveUnit(g,e) set gnum= gnum+1 set e = null endloop set chose= GetRandomInt(0,gnum-1) loop exitwhen ( i > gnum-1) if( i != chose ) then set u[i] = null endif set i = i+1 endloop call DestroyGroup(g) set g = null return u[chose] endfunction Phần loop chưa set e = null, leak thì phải Tự hỏi sao không dùng luôn call GroupPickRandomUnit(g) mà phải tạo ra 1 func con thế này... Và cho hỏi thêm vậy cúi cùng còn 1 biến u[chose] là không được set null, vậy leak phải không ?
cái này khác gì GroupPickRandomUnit đâu? GroupPickRandomUnit là BJ có ích, việc gì phải viết lại. Mã: loop set e = FirstOfGroup(g) exitwhen e == null set u[gnum] = e call GroupRemoveUnit(g,e) set gnum= gnum+1 [B][COLOR="Red"]set e = null[/COLOR][/B] endloop khi đã exitwhen e==null rồi thì ko cần null e nữa, bởi vì ra khỏi vòng lặp khi nó là null => khi hết lặp thì nó cũng null và cái dòng đỏ là thừa. array u leak vì không được null (cả array đó, dùng bao nhiêu index thì leak bấy nhiêu biến) ngoài ra, indent sai rồi. ------- và "BJ có hại" ở đây là các function chỉ làm mỗi nhiệm vụ là gọi một native => nên dùng native luôn (ví dụ function stop ở trên) còn "BJ có ích" thì như là GroupPickRandomUnit hay function grouprandomunit ở trên (nếu không leak)
uầy em ko biết có cái GroupPickRandomUnit nên mới phải viết , chứ có viết lại đâu còn vụ set null cái array , em quên mất ! thanks anh nhe. À mà thôi, sửa làm j` nữa, có cái BJ rồi , xóa luôn .
Cái hàm add time life hay chứ anh. Tại bt vd em muốn add time life cho unit,cái đoạn add thể loại time life gì cho unit toàn quên,vì nó là mã raw,giờ viết lại hàm chỉ cần call ATL(u,2) là xong,cũng tiện hơn.Mà cái "hại" này có ghê gớm quá ko anh? Hay chỉ chậm hơn 1 chút thôi?
Tác hại chắc chẳng ghê gớm lắm đâu. Tại tôi thường test spell theo kiểu mass 25 unit cast cùng lúc mà, spell nào gây leak biết ngay. Library tôi dùng rất rất nhiều hàm custom. Tác hại thì chưa thấy đâu, cái lợi là khi gọi hàm cực đơn giản , làm spell, làm system nhanh hơn.
video 26 unit cast cùng lúc 1 ultimate, cast 3 lần http://www.mediafire.com/?2rkf146p3sm5syr Thông tin ultimate : tạo 2 effect lửa phoenix ở 2 tay hero, cho nó dash ( ko phải blink nhé , dash là thấy nó di chuyển từ từ từ vị trí A đến B đấy ) , dash ra phía sau thằng ăn chưởng, chém lén nó. Sau mỗi cú chém thì hiện dmg lên, và có effect nổ . Chém tổng cộng 5 cú, cú dứt điểm là nó tách ra làm 2 , 1 nó , và 1 cái bóng, dash 1 cái xoẹt theo hình dấu X , quay lưng về phía thằng bị đòn, nghênh mặt lên trời làm như ta đây vô đối. Thằng ăn đòn nhận 1 cú nổ lửa to đùng làm kỷ niệm . Cái bóng từ từ fade out .... sau khi cast nhìn con chuột di chuyển xem thấy là biết có leak hay ko .... Nếu làm đúng theo hướng dẫn trên của tôi, các bạn cứ làm spell hoành tráng thoải mái đê
Hồi xưa tớ code jass rối lắm, không có ntn đâu. Mấy lần nhờ anh Tom xem code, ảnh kêu nên từ hồi đó mới bắt đầu đi học cách trình bày (xem code của người ta, tự mình trình bày lại theo cách của mình). Mà tớ thì hay trình bày theo cách này: Mã: function Example takes nothing returns nothing local unit c local unit t //Cùng là "biến" unit, không cách dòng local integer i //Sang đến đây là "biến" integer, cách 1 dòng local real r ....etc endfunction Và ở trong loop hoặc if thì "TAB" 1 lần để nhìn cho rõ: Mã: function Example takes nothing returns nothing .......... set i = 0 loop exitwhen i > 5 ....... set i = i + 1 endloop endfunction P/s: Lúc tớ đọc tên topic cứ ngỡ là topic này chỉ "dạy" cách trình bày code Jass thôi, ra là có cả remove leak. P/s2: Bài đầu bị lỗi cái kìa, phần bôi đen phần không .