Posté le: 13/03/09 17:46 Sujet du message: Crash du jeu à cause d'un déclencheur
J'ai (comme toujours) un problème de VJass.
J'ai une unité qui possède deux sorts passifs à chance.
Le premier fonctionnait très bien (en gros quand l'unité attaque un ennemi, il y a 20% de chances de projeter celle-ci avec un effet graphique).
L'unité dispose du sort dès le début;
Je lui donne un deuxième sort passif (en cours de jeu).
Celui ci consiste à faire élever l'unité à la verticale, à l'endommager, et à la replacer aléatoirement sur la carte tout en lui redonnant instantanément une hauteur normale.
Sauf que même si le JNPG ne détecte aucune erreur, le jeu crash quand mon unité attaque un ennemi.
J'ai essayé de mettre des noms de variables et cie clairs et mis quelques commentaires pour rendre mon sort compréhensible.
Merci d'avance.
Secret:
Jass:
scope forceunleash initializer init
//==========================================================================================
globals
private constant real TIMEOUT = 0.03 //Période du timer
private constant string FX = "Abilities\\Spells\\Human\\Resurrect\\ResurrectTarget.mdl" //Effet spécial
private constant integer RAVEN = 'Amrf' //Forme de corbeau
private constant integer FORCEUNLEASH = 'A02B' //ID du sort Force Unleash
endglobals
//==========================================================================================
struct structure
integer i
unit target
real dmg
unit caster
endstruct
//==========================================================================================
private function periodic takes nothing returns nothing
local timer t = GetExpiredTimer()
local structure data = GetTimerData(t)
local real z1 = GetUnitFlyHeight(data.target)
local real z2 = z1+5.00
if (data.i < 50) then //Sert à faire monter la cible à la verticale
set data.i = data.i + 1
call SetUnitFlyHeight(data.target,z2,0.00)
else
//On endommage la cible
call UnitDamageTargetBJ( data.caster, data.target, data.dmg, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL )
//On l'envoie à une destination aléatoire
call SetUnitPosition(data.target,I2R(GetRandomInt(1000,4000)),I2R(GetRandomInt(1000,4000)))
//On la baisse instantannément au sol
call SetUnitFlyHeight(data.target, 0.00,0.00)
//On lui redonne la collision
call SetUnitPathing(data.target,false)
//on lui enlève forme de crobeau
call UnitRemoveAbility(data.target,RAVEN)
//On recycle le timer et détruit la structure
call ReleaseTimer(t)
call data.destroy()
endif
private function INIunleash takes unit caster, unit target returns nothing
//Fonction d'initialisation du timer
local timer t = NewTimer()
local structure data = structure.create()
set data.i = 0
//l'attaquant
set data.caster = caster
//l'unité attaquée
set data.target = target
//les dégâts qui seront infligés
set data.dmg = GetHeroStr(data.caster,true)
//on enlève à l'unité attaquée la collision et on lui donne forme de corbeau
call SetUnitPathing(data.target,false)
call UnitAddAbility(data.target,RAVEN)
//On créé un effet spécial sur la position de l'unité attaquée
call DestroyEffect( AddSpecialEffect(FX,GetUnitX(data.target),GetUnitY(data.target)))
//On lance le timer
call SetTimerData(t, data)
call TimerStart (t, TIMEOUT, true, function periodic )
endfunction
//=================================================
//Le déclencheur classique
//=================================================
private function Conditions takes nothing returns boolean
//Ne fonctionne que si l'attaquant est cette unité
if ( not ( GetUnitTypeId(GetAttacker()) == 'O026' )) then
return false
endif
return true
endfunction
//=================================================
private function Actions takes nothing returns nothing
local unit target = GetTriggerUnit() //L'unité attaquée
local unit caster = GetAttacker() //L'attaquant
local integer i = GetRandomInt(1,100) //L'entier qui permet ou non de lancer les actions
local real x1 = GetUnitX(caster)
local real y1 = GetUnitY(target)
//Facteur pour une fonction tierce (recul)
local real imp = 0.75
//Idem
local integer dis = 600
//Idem
local integer speed = 30
if i > 80 then //20% de chances d'appeller Recul et Eclair (fonctions sans problème)
call Recul(x1, y1, target, dis, speed, imp)
call eclair (caster,target,"AFOD",1.00)
endif
//Si le niveau du sort FORCEUNLEASH est supérieur à 1, 5% de chances de lancer l'initialisation du timer
if i <= 5 and GetUnitAbilityLevelSwapped(FORCEUNLEASH,caster) >= 1 then
call INIunleash (caster,target)
endif
set caster = null
set target = null
endfunction
//=================================================
//Déclaration du scope
public function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ATTACKED )
call TriggerAddCondition(t, Condition(function Conditions))
call TriggerAddAction(t, function Actions)
endfunction
//=================================================
endscope
J'ai fait le test, ça ne change rien. Le jeu crash.
C'est vraiment étrange, si le déclencheur est désactivé, aucun bug.
Si je l'active, bug.
Si je supprime l'appel d'une des deux fonctions appelant un des deux sorts, bugs, et ce quelque soit la fonction.
Si ça peut vous être utile, je poste les deux autres fonctions (qui marchaient jusqu'à ce que je fasse ce sort :/
Là en revanche les noms de variables ne sont pas très pratiques pour d'autres logiques que la mienne :/
Recul (toujours pareil, la fonction de Rhad optimisée par mes besoins
private function Action takes nothing returns nothing
local timer t = GetExpiredTimer()
local recul_st st = GetTimerData(t)
local real newX = GetUnitX(st.target) + st.vit*Cos(st.angle)
local real newY = GetUnitY(st.target) + st.vit*Sin(st.angle)
local real z = GetUnitFlyHeight(st.target)
local real newz = 0.00
local string fx
if (st.i < st.distance) and GetTerrainCliffLevel(newX,newY)<=4 and RectContainsUnit(gg_rct_arena,st.target ) == true then
set st.i = st.i + 1
if GetTerrainCliffLevel(newX,newY)>=1 then
call SetUnitPosition(st.target, newX, newY)
endif
if st.imp > 0.00 and GetTerrainCliffLevel(newX, newY)<4 then
if (st.i < (st.distance * 0.5)) then
set newz = z + (st.distance * st.imp)
else
set newz = z - (st.distance * st.imp)
endif
call SetUnitFlyHeight(st.target, newz, 0.00)
else
endif
if newz > 0.00 then
set fx = fx_air
call DestroyEffect(AddSpecialEffectTarget(fx, st.target, "origin"))
else
if not (IsTerrainPathable(newX,newY,PATHING_TYPE_FLOATABILITY)) then
set fx = fx_eau
call DestroyEffect(AddSpecialEffectTarget(fx, st.target, "origin"))
else
endif
if not (IsTerrainPathable(newX,newY,PATHING_TYPE_WALKABILITY)) then
set fx = fx_sol
call DestroyEffect(AddSpecialEffectTarget(fx, st.target, "origin"))
else
endif
endif
else
call SetUnitPathing( st.target, true)
call UnitRemoveAbility( st.target, RAVEN)
call ReleaseTimer(t)
call st.destroy()
endif
set fx = null
endfunction
function Recul takes real x, real y, unit target, integer distance, integer vit, real imp returns nothing
local timer t = NewTimer()
local recul_st st = recul_st.create()
call SetUnitPathing( target, false)
set st.target = target
set st.angle = Atan2(GetUnitY(target) - y, GetUnitX(target) - x)
set st.distance = distance/vit
set st.vit = vit
set st.i = 0
set st.imp = imp
call SetTimerData(t, st)
call TimerStart (t, TIMEOUT, true, function Action )
call UnitAddAbility(st.target, RAVEN)
private function Action takes nothing returns nothing
local timer t = GetExpiredTimer()
local laser la = GetTimerData(t)
local real x1 = GetUnitX(la.u1)
local real y1 = GetUnitY(la.u1)
local real z1 = GetUnitFlyHeight(la.u1)
local real x2 = GetUnitX(la.u2)
local real y2 = GetUnitY(la.u2)
local real z2 = GetUnitFlyHeight(la.u2)
if (la.i < la.dur) and IsUnitAliveBJ(la.u2)==true then
set la.i = la.i + 1
call MoveLightningEx(la.e,true,x1, y1, z1, x2, y2, z2 )
else
call DestroyLightning(la.e)
call ReleaseTimer(t)
call la.destroy()
endif
endfunction
function eclair takes unit u1,unit u2, string s, real dur returns nothing
local timer t = NewTimer()
local laser la = laser.create()
local real x1 = GetUnitX(u1)
local real y1 = GetUnitY(u1)
local real z1 = GetUnitFlyHeight(u1)
local real x2 = GetUnitX(u2)
local real y2 = GetUnitY(u2)
local real z2 = GetUnitFlyHeight(u2)
local lightning e = AddLightningEx(s, true, x1, y1, z1+50.00, x2, y2, z2+50.00)
set la.u1 = u1
set la.u2 = u2
set la.e = e
set la.i = 0
set la.dur = R2I(dur/TIMEOUT)
call SetTimerData(t, la)
call TimerStart (t, TIMEOUT, true, function Action )
call DestroyLightning(e)
set e = null
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: 13/03/09 21:20 Sujet du message:
Précise la nature du crash.
War 3 se ferme sans aucun message d'erreur -> boucle sans fin d'appel de fonction.
avec un message d'erreur -> operation interdite, argument invalide, voir éventuellement un bug avec l'éditeur d'objet. _________________
Bah c'est pas compliqué, le screenshot que je viens de t'envoyer est un bug dès le lancement de la fonction Actions: en effet j'ai passé l'appel de la fonction INIunleash en commentaire.
Je vais quand même faire ce que tu dis en remettant sans // l'appel de la fonction. _________________
//=================================================
private function Conditions takes nothing returns boolean
if ( not ( GetUnitTypeId(GetAttacker()) == 'O026' )) then
return false
endif
return true
endfunction
//=================================================
private function Actions takes nothing returns nothing
local unit target = GetTriggerUnit()
local unit caster = GetAttacker()
local integer i = GetRandomInt(1,100)
//======
call TriggerSleepAction(1.00)
call BJDebugMsg("1")
//======
if i <= 5 and GetUnitAbilityLevelSwapped(FORCEUNLEASH,caster) >= 1 then
//======
call TriggerSleepAction(1.00)
call BJDebugMsg("2")
//======
call INIunleash (caster,target)
endif
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: 13/03/09 23:00 Sujet du message:
Ah ouais mais nan ...
Tu ne peux pas utiliser de waits dans des callback (timer, ForGroup, etc) et uniquement dans une action de trigger ou (je crois et la flemme de tester) dans une fonction appelée avec ExecuteFunc, et bien sûr une fonction appelée avec .execute(), étant donné que le code compilé résultant est une exécution de la partie actions d'un trigger.
Je parlais simplement de ta fonction Actions déjà, c'est à dire ta fonction principale, avant d'aller plus loin dans tes fonctions secondaires. _________________
Oui mais ce que je disais au dessus, c'est que même sans les messages de debug et les wait que j'ai rajouté pour retester, avant, en ajoutant un // devant l'appel de la fonction INIunleash (situé dans la fonction Actions), ça plante dès que mon unité attaque un ennemi. _________________
Bon je ne comprends pas pourquoi ça fonctionnait (la fonction eclair) avec la version 2.04 et pourquoi plus maintenant, mais en tout cas le fait de virer le DestroyLightning et la nullification fonctionne. _________________
Sauf la dernière: j'avoue ne pas voir la différence .
Jass:
function IsUnitAliveBJ takes unit whichUnit returns boolean
return not IsUnitDeadBJ(whichUnit)
endfunction
Jass:
function IsUnitDeadBJ takes unit whichUnit returns boolean
return GetUnitState(whichUnit, UNIT_STATE_LIFE) <= 0
endfunction
Jass:
constant native GetUnitState takes unit whichUnit, unitstate whichUnitState returns real
Quand tu codes, il faut éviter au maximum les appels de fonctions inutiles. Là, avec IsUnitAliveBJ, il y en a 2 :
IsUnitAliveBJ appelle une autre fonction (IsUnitDeadBJ), qui appelle une troisième fonction (GetUnitState). En utilisant directement GetUnitState, tu économises 2 appels de fonctions. Ça sert juste à optimiser le code en évitant à ton ordi 2 fonctions. Bon c'est vrai que c'est rien, mais c'est comme ça
Bref, il faut éviter au maximum les BJ qui sont pour la plupart inutiles :
Jass:
function GetUnitStateSwap takes unitstate whichState, unit whichUnit returns real
return GetUnitState(whichUnit, whichState)
endfunction
Par exemple, celle là inverse juste les arguments .. _________________
L'optimisation cela se fait lorsqu'il y en a besoin sur des zones critiques. L'important est avant tout de faire un code qui fonctionne et lisible.
Après il est vrai que la plupart des fonctions BJ n'ont été créées que pour le GUI. _________________
Toutes les heures sont au format GMT + 1 Heure Aller à la page 1, 2, 3Suivante
Page 1 sur 3 La question posée dans ce topic a été résolue !
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