Inscrit le: 12 Nov 2011 Messages: 1062 Sujets: 107 Spécialité en worldedit: Inactif(Enfin presque) Médailles: 1 (En savoir plus...)
Posté le: 16/04/16 17:18 Sujet du message: Problème de Dynamic Indexing [VJASS]
Contexte: J'ai fait une bibliothèque qui gère la création et le déplacement de projectile qui partent en ligne droite puis reviennent vers leur utilisateur, infligeant des dégâts à toute unité touchée.
Problème: Lorsqu'on crée une instance et qu'une autre est déjà en cours, la première se fige et l'autre voit son paramètre de vitesse augmenté.
Infos: J'ai droit à un "Attempt to destroy a null struct of type:" Chaque fois qu'une instance(même quand y'a qu'une seule) se termine
struct Disc
private unit dummy
private unit caster
private unit pointer
private real target_x
private real target_y
private real dummy_x
private real dummy_y
private real angle
private boolean comeback
private real speed
private real distance
private player owner
private real damage
private static integer count = -1
private static Disc array Instance
private static method loop_disc takes nothing returns nothing
local integer i = 0
local real x = 0.00
local real y = 0.00
loop
if Instance[i].comeback == FALSE then
if Instance[i].distance >= 0.00 then
set Instance[i].dummy_x = Instance[i].dummy_x + Instance[i].speed * Cos(Instance[i].angle)
set Instance[i].dummy_y = Instance[i].dummy_y + Instance[i].speed * Sin(Instance[i].angle)
set Instance[i].distance = Instance[i].distance - Instance[i].speed
call SetUnitX(Instance[i].dummy, Instance[i].dummy_x)
call SetUnitY(Instance[i].dummy, Instance[i].dummy_y)
if TRAINING == TRUE then
call SetUnitX(Instance[i].pointer, Instance[i].dummy_x)
call SetUnitY(Instance[i].pointer, Instance[i].dummy_y)
endif
else
set Instance[i].comeback = TRUE
endif
else
set Instance[i].target_x = GetUnitX(Instance[i].caster)
set Instance[i].target_y = GetUnitY(Instance[i].caster)
set x = Instance[i].target_x - Instance[i].dummy_x
set y = Instance[i].target_y - Instance[i].dummy_y
if SquareRoot(x * x + y * y ) > CST_AREA_MEDIUM then
set Instance[i].angle = Atan2(y, x)
set Instance[i].dummy_x = Instance[i].dummy_x + Instance[i].speed * Cos(Instance[i].angle)
set Instance[i].dummy_y = Instance[i].dummy_y + Instance[i].speed * Sin(Instance[i].angle)
call SetUnitX(Instance[i].dummy, Instance[i].dummy_x)
call SetUnitY(Instance[i].dummy, Instance[i].dummy_y)
if TRAINING == TRUE then
call SetUnitX(Instance[i].pointer, Instance[i].dummy_x)
call SetUnitY(Instance[i].pointer, Instance[i].dummy_y)
endif
else
call KillUnit(Instance[i].dummy)
if TRAINING == TRUE then
call RemoveUnit(Instance[i].pointer)
endif
if i < count then
set Instance[i].dummy = Instance[count].dummy
set Instance[i].caster = Instance[count].caster
set Instance[i].distance = Instance[count].distance
set Instance[i].owner = Instance[count].owner
set Instance[i].speed = Instance[count].speed
set Instance[i].dummy_x = Instance[count].dummy_x
set Instance[i].dummy_y = Instance[count].dummy_y
set Instance[i].target_x = Instance[count].target_x
set Instance[i].target_y = Instance[count].target_y
set Instance[i].angle = Instance[count].angle
set Instance[i].comeback = Instance[count].comeback
set Instance[i].damage = Instance[count].damage
if TRAINING == TRUE then
set Instance[i].pointer = Instance[count].pointer
endif
set Instance[count].dummy = null
set Instance[count].caster = null
set Instance[count].owner = null
call Instance[count].deallocate()
else
set Instance[i].dummy = null
set Instance[i].caster = null
set Instance[i].owner = null
call Instance[i].deallocate()
endif
set count = count - 1
if count == -1 then
call PauseTimer(TIMER)
endif
endif
endif
set i = i + 1
exitwhen i > count
endloop
endmethod
static method create_disc takes unit c, integer id, player o, real tx, real ty, real s, real d returns nothing
local real cx = GetUnitX(c)
local real cy = GetUnitY(c)
local real dx = 0.00
local real dy = 0.00
local real a = Atan2(ty - cy, tx - cx)
set count = count + 1
call Instance[count].allocate()
set Instance[count].caster = c
set Instance[count].dummy_x = cx + 60.00 * Cos(a)
set Instance[count].dummy_y = cy + 60.00 * Sin(a)
set Instance[count].dummy = CreateUnit(o, id, Instance[count].dummy_x, Instance[count].dummy_y, a)
if TRAINING == TRUE then
set Instance[count].pointer = CreateUnit(o, CST_POINTER_MEDIUM_ID, Instance[count].dummy_x, Instance[count].dummy_y, a)
endif
set Instance[count].angle = a
set dx = tx - Instance[count].dummy_x
set dy = ty - Instance[count].dummy_y
set Instance[count].speed = s
set Instance[count].comeback = FALSE
set Instance[count].owner = o
set Instance[count].distance = SquareRoot(dx * dx + dy * dy )
set Instance[count].damage = d
if count == 0 then
call TimerStart( TIMER, CST_TIME, true, function Disc.loop_disc )
endif
endmethod
endstruct
endlibrary
Notes: J'utilise dans la map une autre bibliothèque qui gère les Knockbacks, comme elles sont codées sur la même base, je la met aussi, on sait jamais.
Secret:
Jass:
library Knockback
globals
private timer TIMER = CreateTimer()
constant real CST_KNOCKBACK_DISTANCE_RATIO = 30.00
constant real CST_KNOCKBACK_SPEED_RATIO = 1.25
endglobals
struct Knockback
private static integer count = -1
private static Knockback array Instance
private unit target
private real speed
private real distance
private real target_x
private real target_y
private real angle
private string model
static method loop_knockback takes nothing returns nothing
local integer i = 0
loop
exitwhen i > count
if Instance[i].distance > 0 then
set Instance[i].distance = Instance[i].distance - Instance[i].speed
call DestroyEffect(AddSpecialEffect(Instance[i].model, Instance[i].target_x, Instance[i].target_y) )
set Instance[i].target_x = Instance[i].target_x + Instance[i].speed * Cos(Instance[i].angle)
set Instance[i].target_y = Instance[i].target_y + Instance[i].speed * Sin(Instance[i].angle)
if GetLocationZ(Location(Instance[i].target_x, Instance[i].target_y)) == GetLocationZ(Location(GetUnitX(Instance[i].target), GetUnitY(Instance[i].target) ) ) then
call SetUnitX( Instance[i].target, Instance[i].target_x )
call SetUnitY( Instance[i].target, Instance[i].target_y )
endif
else
if i < count then
set Instance[i].target = Instance[count].target
set Instance[i].distance = Instance[count].distance
set Instance[i].speed = Instance[count].speed
set Instance[i].angle = Instance[count].angle
set Instance[i].target_x = Instance[count].target_x
set Instance[i].target_y = Instance[count].target_y
set Instance[i].model = Instance[count].model
call Instance[count].deallocate()
else
call Instance[count].deallocate()
endif
set count = count - 1
if count == -1 then
call PauseTimer(TIMER)
endif
endif
set i = i + 1
endloop
endmethod
static method create_knockback takes unit t, real s, real d, real x, real y, real x2, real y2, string m returns nothing
local real a = Atan2(y2 - y, x2 - x)
set count = count + 1
set Instance[count] = Instance[count].allocate()
set Instance[count].target = t
set Instance[count].speed = s
set Instance[count].angle = a
set Instance[count].target_x = x2
set Instance[count].target_y = y2
set Instance[count].distance = d
set Instance[count].model = m
if count == 0 then
call TimerStart( TIMER, CST_TIME, true, function Knockback.loop_knockback )
endif
endmethod
*Renomme toutes les méthodes create_ en create (ce n'est pas qu'esthétique).
*Dans la librairie Knockback
Jass:
set Instance[count] = Instance[count].allocate()
doit être
Jass:
set Instance[count] = thistype.allocate()
allocate est une static method et non une method. De plus tu peux utiliser thistype pour indiquer le nom de ta struct (ce que tu devrais faire quand tu définis Instance).
D'ailleurs, les appellation thistype. sont optionnels à l'intérieur de la struct. tu peux directement écrire : set Instance[count] = allocate(). De même pour les variables static (ce que tu fais pour count d'ailleurs).
*Dans la librairie Disc
Jass:
call Instance[count].allocate()
doit être
Jass:
set Instance[count] = thistype.allocate()
Je ne sais pas pourquoi tu ne reprends pas la syntaxe de Knockback. Tu ne sauvegardes jamais l'instance dans Instance[count] en faisant ainsi. _________________
Inscrit le: 12 Nov 2011 Messages: 1062 Sujets: 107 Spécialité en worldedit: Inactif(Enfin presque) Médailles: 1 (En savoir plus...)
Posté le: 17/04/16 14:07 Sujet du message:
@Wareditor:
-Je les nomme create_bidule et pas create parce que la method create renvoie le type auquel elle est liée, ce qui en fait une right-value potentielle. Or, je préfère m'assurer qu'on ne puisse pas récupérer les instances autre part que dans la librairie donc mon choix n'est pas qu'esthétique.(Après si y'a un moyen de passer outre ce détail, je peux évidemment renommer en create.)
-Pour ce qui est de Disc, j'avais en tête que les deux syntaxes produisaient le même résultat du coup j'avais pas pensé que l'erreur pouvait venir de là.
-En ce qui concerne le changement vers thistype, c'est fait. _________________
Avec le contenu des librairies actuelles, même si on pourrait récupère l'instance d'une struct on ne peut rien faire avec (toutes les variables sont private et il n'y a aucune méthodes non static). Si tu introduit des méthodes que tu ne veux pas qu'un utilisateur utilise il suffit de les rendre private (ce que tu devrais faire pour ta fonction boucle).
Sinon la manière dont tu enlève les instances achevées me semble imparfaite. J'ai l'impression que avec ta méthode (je parle pour Knockback mais j'assume que la méthode est similaire pour Disc) à chaque fois qu'une instance arrive à sa fin, tu n'appliques pas de changement à la précédemment dernière instance. Puisque la jadis dernière instance prend la place de l'instance supprimée et se fait donc ignorer dans cette itération (car jamais atteinte).
D'ailleurs aucune utilité à remplace toutes les variables de Instance[i] par les variables de Instance[count]. Tu peux directement faire Instance[i]=Instance[count] (cela ne corrigera pas l'imperfection mentionnée plus haut). _________________
Inscrit le: 12 Nov 2011 Messages: 1062 Sujets: 107 Spécialité en worldedit: Inactif(Enfin presque) Médailles: 1 (En savoir plus...)
Posté le: 17/04/16 21:51 Sujet du message:
J'ai test et ça marche correctement.
J'ai effectivement pas fait de Get/Set et autres car pour l'instant c'est juste des bibliothèques de base dont la seule method que l'utilisateur a le droit d'appeler est create_ (c'est pour ça qu'elle n'est pas private)
Les instances achevées sont enlevées de manière imparfaite ? Pourtant je nullifie les handles(quand y'en a), stocke les valeurs de l'instance la plus haute dans l'instance qui se termine comme ça je n'ai pas d'élément vide dans ma boucle et libère l'espace alloué en faisant un deallocate.
Pour le Instance[i] = Instance[count], si j'ai pas fait ça c'est parce que j'ai plus le fonctionnement du C++(à base de pointeurs ofc) que celui du vJASS en tête en ce moment et que du coup, j'avais en tête que j'allais les faire pointer vers les même valeurs d'attributs et qu'avec le deallocate du coup ils pointeraient sur rien.
Je changerais ça demain/dans la semaine.
Sujet résolu du coup. (même si la discussion n'est pas close pour autant à mon avis)
P.S: Tiens d'ailleurs y'a pas de pointeurs en vJASS ?(sauf si je me trompe) _________________
Inscrit le: 23 Aoû 2007 Messages: 7143 Sujets: 147 Spécialité en worldedit: le troll, le flood, la vulgarité, mon coeur balance Médailles: 2 (En savoir plus...)
Posté le: 18/04/16 16:10 Sujet du message:
Non en effet pas de pointeur en vJass, on ne peut utiliser des variables que par valeur, pas par référence.
Mais ca pourrait se faire dans le préprocesseur (du moins avec les variables globales, portée de la variable toussa toussa) _________________
Le problème vient du fait que tu stocke les valeurs de l'instance la plus haute dans celle qui se fait remplacée. La boucle actuelle n'atteindra donc jamais l'instance la plus haute (car count est diminué) et l'objet de l'instance la plus haute ne subira aucune transformation dans cette occurence de loop_knockback.
Je visualise ça comme cela :
Code:
Cas d'une boucle avec 4 instances
*Début de la boucle
**applique des transformations à l'objet de l'instance 1
**applique des transformations à l'objet de l'instance 2
->l'instance 2 est terminée
->l'instance 4 devient l'instance 2 et le nombre total d'instance passe à 3
**applique des transformations à l'objet de l'instance 3
*Fin de la boucle
Sinon, je ne suis plus sur si nullifier toutes les variables à la main est redondant ou non. Je ne sais plus si la static destroy ne le fait pas déjà. Il faudrait que quelqu'un de plus connaisseur s'exprime la dessus. _________________
Vous ne pouvez pas poster de nouveaux sujets dans ce forum Vous ne pouvez pas répondre aux sujets dans ce forum Vous ne pouvez pas éditer vos messages dans ce forum Vous ne pouvez pas supprimer vos messages dans ce forum Vous ne pouvez pas voter dans les sondages de ce forum