[Tutorial] Basics of Structs

Tom_Kazansky

<b><font color="RoyalBlue"><font face="Lucida Cons
Tham gia ngày
28/12/06
Bài viết
3,454
Reaction score
462
Trong tutorial này, tôi (Andrewgosu) sẽ giới thiệu cơ bản về sử dụng struct. Làm thế nào để tạo, xóa, "gắn" (attach = gắn) và các thao tác với struct.

Để tiếp tục, bạn cần:
- World Editor
- Jass NewGen Pack
- Hiểu biết ở mức trung bình về Jass

Các bước mà tôi định dạy:
- Struct là gì và làm thế nào để khai báo một struct
- Thêm các "component" ( "thành phần", nghe ko hay lắm, hay dịch là "phần tử" đi) cho một struct
- Tạo một struct trong một hàm và truy nhập các phần tử của một struct
- "gắn" struct vào một "handle" dùng các hệ thống "gắn" để nhận lại struct đã "gắn"
- Xóa một struct
- Kết thúc

Bước 1 - Struct là gì và làm thế nào để khai báo một struct

- struct là gì ?
To put it simple, structs are parallel globals arrays with a nice indexing system. No game cache involved, whatsoever.

Because structs are globals arrays, they have an instance limit, 8191, if to be exact (Fancy trivia, god I love it). But don't be frightened, as you will normally not meet this limit.

Khai báo struct thì rất đơn giản: đầu tiên, bạn cần gõ từ khóa "struct", sau đó gõ tên struct và cuối cùng đóng struct với từ khóa "endstruct"

Ví dụ:

Mã:
struct structname
endstruct

Thế là ta đã có struct tên là structname

Bước 2 - Thêm các "phần tử" cho một struct

Một struct mà trống thì chả sử dụng đc gì.
Sau khi khai báo struct, bạn phải thêm các phần tử cho nó - là những thứ mà bạn muốn "lưu"
Và việc này còn dễ hơn khai báo struct

Mã:
struct structname
    unit whichPanda
    real facing
    real x
    real y
    integer level
endstruct

Các phần tử của struct có thể có giá trị khởi tạo:

Mã:
struct structname
    real fullAngle = 90.
    integer maxNumber = 69
endstruct

Về "mảng" trong một struct, ko có vấn đề gì, nhg thật ra có một
Khi khai báo "mảng" trong một struct, bạn phải đặt "array index limit" (giới hạn chỉ số của mảng)

Mã:
struct structname
    integer array level[100]
endstruct

Điều này có nghĩa là bạn chỉ có thể sử dụng level với chỉ số mảng từ 0 -> 99, nếu bạn dùng với 100 hoặc hơn thì khi kiểm tra code, sẽ có lỗi đc báo.

Bước 3 - Tạo một struct trong một hàm và truy nhập các phần tử của một struct

Để tạo một struct, sẽ có cũ pháp vJass. Nhưng ko nên thấy lo và nhầm lẫn

Mã:
function functionname takes nothing returns nothing
    local structname data = stuctname.create()
endfunction

Tôi chắc là "local" quá quen thuộc với bạn rồi, nhg sao "structname" lại là type của local variable này vì ko có type "structname" mà chỉ có unit, integer, real,..

Đúng, nhg bây giờ thì có rồi.
Tên của struct mà bạn khai báo, bạn phải dùng tên nó như là type của variable khi tạo nó trong một hàm.
Ví dụ:

Mã:
function functionname takes nothing returns nothing
    local pandamonium data = pandamonium.create()
    local thehelper info = thehelper.create()
    local apple worm = apple.create()
endfunction

"pandamonium", "thehelper" và "apple" là tên struct, và "data", "info", "worm" chỉ là tên của struct đc tạo ra.

Sau khi tạo struct, bạn có quyền truy nhập vào các phần tử của nó. Và hãy chú ý đến cũ pháp:

Mã:
function functionname takes nothing returns nothing
    local structname data = stuctname.create()
    local unit caster = GetTriggerUnit()
    
    set data.whichPanda = caster
    set data.facing = GetUnitFacing(caster)
    set data.x = GetUnitX(caster)
    set data.y = GetUnitY(caster)
    set data.level = GetHeroLevel(caster)
endfunction

Bạn dùng struct có tên "data" (tên này do bạn đặt, là gì cũng đc) để truy nhập các phần tử của struct có tên là "structname"

Thêm vài ví dụ:

Mã:
function functionname takes nothing returns nothing
    local haxxor gamer = haxxor.create()
    set gamer.realX = 270.
    set gamer.RealY = 90.
endfunction

function functionname takes nothing returns nothing
    local sheep grass = sheep.create()
    set grass.killer = GetKillingUnit()
endfunction

Bước 4 - "gắn" struct vào một "handle" dùng các hệ thống "gắn" để nhận lại struct đã "gắn"

Đã có nhiều "hệ thống gắn/liên kết". Tôi sẽ sử dụng các hệ thống thông dụng
Tôi sẽ "gắn" struct vào một timer tên là "callback", timer này có "function callback" là callbackfunc


Kattana's Handle Variables

Mã:
function callbackfunc takes nothing returns nothing
    local structname data = GetHandleInt(callback, "whichStruct")
endfunction

function functionname takes nothing returns nothing
    local structname data = structname.create()
    call SetHandleInt(callback, "whichStruct", data)
endfunction

Vexorian's CSData

Mã:
function callbackfunc takes nothing returns nothing
    local structname data = GetCSData(callback)
endfunction

function functionname takes nothing returns nothing
    local structname data = structname.create()
    call SetCSData(callback, data)
endfunction

Cohadar's ABC

Mã:
function callbackfunc takes nothing returns nothing
    local structname data = GetTimerStructA(callback)
endfunction

function functionname takes nothing returns nothing
    local structname data = structname.create()
    call SetTimerStructA(callback, data)
endfunction
Cũng ko quá khó.

Bước 5 - Xóa một struct

Struct phải bị xóa, (location phải bị remove, group phải bị destroy,... )
Ko xóa struct, đơn giản là leak

Cú pháp để xóa một struct:

Mã:
function callbackfunc takes nothing returns nothing
    call data.destroy()
endfunction

Vài ví dụ nữa:

Mã:
function callbackfunc takes nothing returns nothing
    call data.destroy()
    call info.destroy()
    call worn.destroy()
endfunction

Bước 6 - Kết thúc

Sau khi đã biết cơ bản về struct, bạn đã biết làm thế nào để tạo, gắn, nhận lại và xóa struct

Ví dụ cuối:

Mã:
// Khai báo struct
struct sheepfall
    // tạo một danh sách các phần tử
    unit whichSheep
    integer SheepLevel
    real facing
endstruct

function callbackfunc takes nothing returns nothing
    // nhận lại struct
    local sheepfall data = GetCSData(GetExpiredTimer())
    // Destroy the struct
    call data.destroy()
endfunction

function funcname takes nothing returns nothing
    local unit sheep = GetTriggerUnit()
    local timer t = CreateTimer()
    // tạostruct
    local sheepfall data = sheepfall.create()
    // đặt giá trị cho các phần tử
    set data.whichSheep = sheep
    set data.SheepLevel = GetHeroLevel(sheep)
    set data.facing = GetUnitFacing(sheep)
    // gắn struct
    call SetCSData(t, data)
    call TimerStart(t, 10., false, function callbackfunc)
endfunction

Credit:
Thanks to Andrewgosu for making this tutorial, this is his original post: http://world-editor-tutorials.thehelper.net/cat_usersubmit.php?view=79426

------------------------------
Đến đây, Tom sẽ dùng struct tạo một hàm Knockback (Tom chọn Knockback vì nó dễ hiểu :) )

Ta cần(nên dùng) CSData và CSSafety của Vexorian.

Tom có struct sau:

Mã:
struct knockbackdata
    unit whichUnit //unit bị knockback
    real angle     //góc knock
    integer tick   //tik tak, tik tak, số lần "move" unit
    real distance  //khoảng cách mỗi lần "move", tức là tống khoảng cách knock là (distance * tick)
    string sfx     //effect knockback
endstruct

Và hàm để "gọi" knockback, hàm này sẽ lấy unit bị knockback, khoảng cách knockback (tổng khoảng cách), góc knock, effect và duration để xác định số tick và xác định khoảng cách mỗi lần "move"

Mã:
function KnockBack takes unit whichUnit, real distance, real angle, real duration, string sfx returns nothing


endfunction

Có struct rồi, tạo struct, nhập giá trị cho các phần tử thôi:

Mã:
function KnockBack takes unit whichUnit, real distance, real angle, real duration, string sfx returns nothing
    local knockbackdata data = knockbackdata.create()
    set data.whichUnit = whichUnit
    set data.angle = angle
    set data.tick = R2I( duration / 0.04 ) //ta sẽ dùng 0.04s timer, vậy tick = duration / 0.04
    set data.distance = distance / data.tick //nhớ là distance trong struct sẽ là khoảng cách mỗi lần "move", vậy nó sẽ = tống khoảng cách / số tick
    set data.sfx = sfx

endfunction

Bây giờ ta tạo callback function cho một timer, tạo timer rồi dùng CSData "gắn" struct vừa tạo vào timer này

Mã:
function KnockBackE takes nothing returns nothing

endfunction

function KnockBack takes unit whichUnit, real distance, real angle, real duration, string sfx returns nothing
    local knockbackdata data = knockbackdata.create()
    local timer ti = NewTimer() // sử dụng CSSafety, dùng NewTimer thay cho CreateTimer    

    set data.whichUnit = whichUnit
    set data.angle = angle
    set data.tick = R2I( duration / 0.04 ) //ta sẽ dùng 0.04s timer, vậy tick = duration / 0.04
    set data.distance = distance / data.tick //nhớ là distance trong struct sẽ là khoảng cách mỗi lần "move", vậy nó sẽ = tống khoảng cách / số tick
    set data.sfx = sfx

    call SetCSData( ti , data ) //struct sẽ đc coi như một integer
    call TimerStart( ti, 0.04, true, function KnockBackE )
     
    set ti = null //local handle -> phải null
endfunction

Sang "callback function" tên là KnockBackE, ta sẽ nhận lại struct đã "gắn", tất nhiên là dùng CSData

Mã:
function KnockBackE takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local knockbackdata data = GetCSData( ti )

endfunction

Vậy là từ struct, ta sẽ có unit, distance, angle, sfx, tick tiến hành knockback thôi:

function KnockBackE takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local knockbackdata data = GetCSData( ti )
    
    local location loc = GetUnitLoc( data.whichUnit ) //Position of whichUnit
    local location loc2 = PolarProjectionBJ( loc, data.distance, data.angle ) //Point with Polar Offset

    call AddSpecialEffectLocBJ( loc2, data.sfx ) //Create Special Effect
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )   // Destroy Special Effect

    call SetUnitPositionLoc( data.whichUnit, loc2 ) //move unit

    call RemoveLocation( loc )
    call RemoveLocation( loc2 )
    set loc = null   //dùng xong các local handle thì phải null, ko thì leak đấy
    set loc2 = null    

    set data.tick = data.tick - 1 //giảm tick

    if data.tick <= 0 then // dùng <= 0 cho an toàn
        call data.destroy()
        call ReleaseTimer( ti ) // sử dụng CSSafety, dùng ReleaseTimer thay cho DestroyTimer
    endif
    set ti = null
endfunction

Rồi, cuối cùng ta sẽ có:

Mã:
struct knockbackdata
    unit whichUnit
    real angle
    integer tick
    real distance
    string sfx
endstruct

function KnockBackE takes nothing returns nothing
    local timer ti = GetExpiredTimer()
    local knockbackdata data = GetCSData( ti )
    
    local location loc = GetUnitLoc( data.whichUnit )
    local location loc2 = PolarProjectionBJ( loc, data.distance, data.angle )

    call AddSpecialEffectLocBJ( loc2, data.sfx )
    call DestroyEffectBJ( GetLastCreatedEffectBJ() )

    call SetUnitPositionLoc( data.whichUnit, loc2 )

    call RemoveLocation( loc )
    call RemoveLocation( loc2 )
    set loc = null
    set loc2 = null    

    set data.tick = data.tick - 1

    if data.tick <= 0 then
        call data.destroy()
        call ReleaseTimer( ti )
    endif
    set ti = null
endfunction

function KnockBack takes unit whichUnit, real distance, real angle, real duration, string sfx returns nothing
    local knockbackdata data = knockbackdata.create()
    local timer ti = NewTimer()

    set data.whichUnit = whichUnit
    set data.angle = angle
    set data.tick = R2I( duration / 0.04 )
    set data.distance = distance / data.tick
    set data.sfx = sfx

    call SetCSData( ti , data )
    call TimerStart( ti, 0.04, true, function KnockBackE )
     
    set ti = null
endfunction

Sử dụng hàm Knockback trên, đơn giản, gọi function KnockBack thôi
Tạo lại cái skill Storm Hammer trong map demo Slide

Mã:
function StormHammerAct takes nothing returns nothing
    local unit c = GetAttacker()
    local unit u = GetTriggerUnit()
    local integer lvl = GetUnitAbilityLevel( c , 'A000' )
    local location loc
    local location loc2
    
    if lvl == 0 or ( lvl > 0 and GetRandomInt(1,100) > 17 ) then //chưa có ability hoặc có nhg chance random > 17
        set c = null
        set u = null
        return
    endif
    call SetUnitAnimation(c,"attack slam")
    call UnitDamageTarget( c, u, 25. * lvl, true, false, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, null )
    
    set loc = GetUnitLoc(c)
    set loc2 = GetUnitLoc(u)
    //gọi hàm Knockback
    call KnockBack( u, 90. + 30*lvl, AngleBetweenPoints(loc,loc2), 0.8, "Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl" )

    call RemoveLocation( loc )
    call RemoveLocation( loc2 )
    set loc = null
    set loc2 = null
    set c = null
    set u = null
    
endfunction

//===========================================================================
function InitTrig_StormHammer takes nothing returns nothing
    set gg_trg_StormHammer = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_StormHammer, EVENT_PLAYER_UNIT_ATTACKED )
    call TriggerAddAction( gg_trg_StormHammer, function StormHammerAct )
endfunction

---
Một ví dụ đơn giản, dễ hiểu, hy vọng đến đây các bạn (những ai đọc Tutorial này) đã hiểu đôi chút về struct, biết cách sử dụng.

Happy Mapping :)>-

P/S: có map demo Tom gửi kèm ở dưới
 

Attachments

hay quá ! Đúng cái em cần đây rồi ! Love you :x
Anh làm tiếp cái nữa về handle đi :;)
Thx so much :x
 
À này, ông chỉ cho tui cái Map Cútom Script để làm ji` đi.
 
Ai có thể chỉ tôi cách dùng cái NewGen WE đi, khó hỉu we'
 
cách dùng ??? như WE bình thường chả có gì khác cả
Cái Custom Script là 1 lệnh bằng jass đưa vào trong GUI, vì trong GUI ko có.
 
anh tom: haha khá lắm:D:D:D:D:D......lâu lâu trở lại thấy box mình khác hẳn...(project đã bị dẹp)...........thanks
 
Anh Tom ơi,có cách nào xây dựng struct = gui ko?

Em định làm skill rồng đuổi thế này:
-Lv 1->5: 1 rồng,6->10: 2 rồng,11->15: 3 rồng,16->20: 4 rồng.
-Khi caster chơi skill vào 1 target<enemy> nào đó,sẽ có các con rồng<dummy unit> được tạo song song nhau trước mặt caster----> | | | |.Mỗi con rồng sẽ có tốc độ di chuyển 400/s.Và được gắn 1 cái custom value là 10.Add expire timer cho nó là 2,5s.Và gắn cho nó 1 cái biến loại unit là target<mục tiêu><dùng struct???>
-Trigger cứ 0,1s,nếu custom value của dummy unit rồng >1 thì move các con rồng này về hướng trước mặt<facing>1 khoảng cách là 40 đồng thời giảm custom value của nó đi 1.Rồng còn 1,5s thì biến mất.
-Lúc này thì cứ 0,1s lại move nó 1 khoảng cách là 40 về hướng giữa nó và target.<có nghĩa là các con rồng bay song song với nhau 1 khoảng cách 400 thì đổi hướng vè mục tiêu của spell cast ở trên>

-Có 1 trigger nữa deal dame khi có unit come within 128 of unit type rồng đó.

Anh chỉ em cách làm struct gắn mục tiêu bay cho rồng sau 1s nó được tạo ra đi?
-Khi cú
-Khi custom value
 
Back
Top