problème SHAPE_CONE

Répondre
Partager Rechercher
Bonjour !

Je fais appel a la communauté parce que je suis en train de scripter un système de catapultes/balistes qui tirent automatiquement.

Toutes les secondes, le script ci dessous va être exécuté sur le plaçable "catapulte" et va sélectionner la cible valide la plus proche de la catapulte (je vais peut être changer ça, parce que ça doit être gourmand ^^ ).
le truc c'est que j'aimerais qu'il ne recherche des cibles que dans un cône devant lui (ce qui est logique, un baliste ne tire pas en arrière...)
Dans le script j'ai volontairement mis SHAPE_SPHERE comme zone de recherche, mais c'est juste parce que je n'ai pas encore trouvé comment faire le cône...

J'en viens au problème que je rencontre :
si je met SHAPE_CONE (au lieu de SHAPE_SPHERE), la recherche se fera dans une sphère autour de la catapulte (apparemment, c'est pas certain)
Si je met SHAPE_SPELLCONE, la recherche se fera bien dans un cône, mais toujours orienté vers l'est de la zone...

C'est peut être tout simple (je ne connais pas encore tous les trucs de l'éditeur ^^)

si c'est encore une fonction buggée ou quelque chose comme ça (ça ne m'étonnerais pas...), y a t'il moyen de faire avec un calcul d'angle?
je pense à quelque chose du genre "si angle entre catapulte-oPC et heading catapulte >= 30° ==> cible invalide"

Voila une partie du script OnUserDefined :

Code:
        //==========>Partie permettant de trouver une cible et de lui tirer dessus
        if(!GetLocalInt(OBJECT_SELF, "nRecharge"))//Si la catapulte a rechargé
            {
            //On prend l'ennemu le plus proche de la catapulte
            object oCible = OBJECT_INVALID;
            float fPlusPetiteDistance = fPorteeMax;
            
            oPC = GetFirstObjectInShape(SHAPE_SPHERE, fPorteeMax, GetLocation(OBJECT_SELF));
            while(GetIsObjectValid(oPC))
                {
                float fDistance = GetDistanceToObject(oPC);
                object oJournal = GetItemPossessedBy(oPC, "journalNODROP");
                
                if( GetIsReactionTypeHostile(oPC, OBJECT_SELF)        //Il faut que la cible soit hostile par rapport a la faction de la catapulte
                    && !GetLocalInt(oJournal, "nAbriteCatapultes")    //Il faut pas que la cible soit abritée
                    && !GetIsDead(oPC)                                 //Il faut que la cible soit vivante
                    && fDistance >= fPorteeMin                        //Il faut que la cible soit a plus de fPorteeMin de la catapulte
                    && fDistance <= fPlusPetiteDistance)            //Il faut que la cible soit plus près que la dernière
                    {
                    oCible = oPC;
                    fPlusPetiteDistance = fDistance;
                    }
                oPC = GetNextObjectInShape(SHAPE_SPHERE, fPorteeMax, GetLocation(OBJECT_SELF));
                }    
                    
            //Lancement de la pierre
            if(oCible != OBJECT_INVALID)    //Si une cible valide a été trouvée
                {
                //On tire
                ActionCastSpellAtLocation(TIR_CATAPULTE, GetLocation(oCible), METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, FALSE);
                
                //On note l'auteur du tir
                SetLocalObject(OBJECT_SELF, "oTireur", oPC);
                
                //Rechargement de la catapulte : elle ne peut tirer que si nPareATirer == 1
                SetLocalInt(OBJECT_SELF, "nRecharge", 1);
                float fRdmTps = IntToFloat(Random(5));//entre 0 et 4
                DelayCommand((fRechargement-2) + fRdmTps, SetLocalInt(OBJECT_SELF, "nRecharge", 0));
                }
            }
Merci d'avance de vos réponses
(si tout marche a merveille, ça finira peut être sur le vault, qui sait? ^^)
Est-ce que le "facing" de ta capulte est bien fixé ? Ça pourrait être le problème dans la forme toujours tournée à l'est puisque par défaut c'est DIRECTION_EAST (0.0). C'est la direction vers laquelle ton objet regarde. Sinon je vois pas trop, je connais pas assez bien ces fonctions.

Par contre je pense que tu aurais plus à gagner en utilisant des fonctions plus simples et en faisant un peu de math sur les données. Grosso modo:
- tu boucles sur GetNearestCreature/Object à partir de la position de ta catapulte
- tu récupères la position de l'objet
- tu fais un changement de repère pour simplifier les calculs (origine = catapulte)
- tu as un triangle rectangle (la cible et la cata sont les deux sommets d'angles non droit)
- l'angle de sommet l'origine c'est de la trigo
- tu vérifies que l'angle est inférieur à ton cône/2 et tu as une cible dans le cône et tu sors de la boucle

Bref, fais un repère d'origine ta catapulte et dont l'axe Y et le milieu du cône de visée, tu places un point fictif de cible. Tu y verras bien plus clair...
Alors d'abord, il est clair qu'utiliser GetNearestObject plutôt que GetFirstObjectInShape est préférable en terme de performances. Pour résumer, le jeu garde une liste permanente des positions des objets que getNearest va appeler, tandis qu'une requête de forme, que ce soit Sphère ou Cone va impliquer la constitution dynamique d'une nouvelle liste. T'as qu'à voir comment le jeu rame quand tu lances un Cône de Froid sur un tas de créatures.

De fait, la méthode de Lv4, Nutella bénisse son nom, me paraît la plus raisonnable. Si tu peines un peu avec les angles, les facings et les vecteurs dans nwn -c'est mon cas- je te conseille de rechercher des librairies comme "x0_i0_position" qui peuvent bien t'aider.

Je précise que dans les conditions de ta boucle, ta cible doit être dans une fourchette de distance je pense : ni trop près, ni trop loin.
Déjà, merci pour vos réponses, rapides et claires en plus

Lv4 : Le facing de la catapulte est bien positionné vers l'avant de celle ci, donc pour ce qui est du cône qui part vers l'est, je ne sais pas trop quoi dire...

Laban : "il est clair qu'utiliser GetNearestObject plutôt que GetFirstObjectInShape est préférable en terme de performances" super, je note
Dans ma boucle, il y a effectivement des conditions de portée : si la cible est trop près ou trop loin, la catapulte refusera la cible.


je me suis penché sur les vectors/angles/positions, comme l'a suggéré lv4 et je crois y avoir compris quelque chose...

note : je ne suis pas passé par de la trigo, mais en fait par une conversion VectorToAngle sur un plan horizontal, puis par une différence entre le l'angle converti et le facing de la catapulte.
Et je n'ai pas trouvé de fonctions qui me convenaient dans "x0_i0_position".


j'ai pas encore fait de tests ingame pour voir si ça marchait (il se fait tard) mais voila ce que ça donne (ce n'est que la partie permettant de trouver une cible):
Code:
float fPorteeMax = GetLocalFloat(OBJECT_SELF, "fPorteeMax");
float fPorteeMin = GetLocalFloat(OBJECT_SELF, "fPorteeMin");

object oPC = OBJECT_SELF;
object oCible = OBJECT_INVALID;
int n = 0; 
int nCibleTrouvee = 0;

vector vPosCata = GetPosition(OBJECT_SELF);
float fFacingCata = GetFacing(OBJECT_SELF);

float fDistancePC;
vector vPosPC;
float fAnglePC;

float fAngleCone;


while(nCibleTrouvee == 0)
    {
    n++;
    
    oPC = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, n);
    
    //si oPC est trop près, on prend une autre oPC plus loin
    fDistancePC = GetDistanceToObject(oPC);
    if(fDistancePC<=fPorteeMin)
        {continue;}    //On retourne au début de la boucle
    
    //Si oPC est trop loin, on arrête la boucle
    if(fDistancePC>=fPorteeMax || !GetIsObjectValid(oPC) )
        {break;}
    
    vPosPC = GetPosition(oPC);
    
    //Changement de repère
    vPosPC = vPosPC - vPosCata;
    
    //On chope l'angle sur le plan (x, y) 
    vPosPC.z = 0.0;
    fAnglePC = VectorToAngle(vPosPC);
    
    //L'angle entre le facing de la cata et la ligne Catapulte-PC
    fAngleCone = fAnglePC-fFacingCata;
    
    //On prend sa valeur absolue
    if(fAngleCone<0.0)
        {fAngleCone=-fAngleCone;}
    
    //Si l'angle est inf à 30°, la cible est validée
    if(fAngleCone <= 30.0)
        {
        nCibleTrouvee = 1;
        oCible = oPC;
        break;
        }
    }
Je peaufinerais tout ça demain et ferais des tests.

Merci pour votre aide ! (je ne vous dirais pas au revoir tant que je n'aurais pas testé ^^)

EDIT : Hum... il faut faire comment pour prendre le facing de la catapulte comme axe des y dans le repère? j'ai un trou ^^

re EDIT : ça ne marche pas... je suspecte le VectorToAngle de ne pas marcher comme je le voudrais...
Dans la librairie indiquée par Laban il me semble qu'il y a une fonction GetAngle(vector origin, vector target, float dist); qui à partir d'un point d'origine (composantes x et y du premier vecteur), d'un second point (composantes x et y du second vecteur) et d'une distance entre les deux renvoie l'angle.

Tu aurais donc une fonction de récupération de cible comme ça:
Code PHP:

object GetNearestTarget(object cata)
{
    
float max GetLocalFloat(cata"fPorteeMax");
    
float min GetLocalFloat(cata"fPorteeMin");
    
    
int nth 1;
    
//1 1 4 1
    
object target GetNearestCreature(CREATURE_TYPE_PLAYER_CHARPLAYER_CHAR_IS_PCcatanthCREATURE_TYPE_IS_ALIVETRUE);
    
    
vector vcata GetPosition(cata);
    
vector vtarget;
    
float distangle;
    while (
target != OBJECT_INVALID)
    {
        
dist GetDistanceBetween(catatarget);
        if (
dist min)
            continue;
        if (
dist max)
            return 
OBJECT_INVALID;
        
        
vtarget GetPosition(target);
        
angle GetAngle(vcatavtargetdist);
        
        if (
angle <= 30.0)
            break;

        
target GetNearestCreature(11cata, ++nth41);
    }
    
    return 
target;

Peut-être que ça marche, peut-être pas. En tout cas je ne l'ai pas passée au compilateur. Tu noteras l'utilisation de GetNearestCreature (qui implique le paramètre OBJECT_TYPE_CREATURE) ainsi que les paramètres IS_PC et IS_ALIVE qui te permettent d'affiner ta recherche.
La fonction renvoie OBJECT_INVALID si elle ne trouve rien.
Le code de LV4 me semble bien optimisé.

Je te propose deux petites fonctions de débguggage que je trouve bien pratique :

Code PHP:

// blue, green, orange 
void DebugObjectLocationobject oObjectstring sColor "yellow" )
{
    
effect eEffect EffectNWN2SpecialEffectFile"fx_gem_energy_" sColor );
    
//
    
ApplyEffectToObjectDURATION_TYPE_TEMPORARYeEffectoObject2.0 );
}



// blue, green, orange 
void DebugLocationobject oObjectstring sColor "yellow" )
{
    
effect eEffect EffectNWN2SpecialEffectFile"fx_gem_energy_" sColor );
    
//
    
ApplyEffectAtLocationDURATION_TYPE_TEMPORARYeEffectGetLocationoObject ), 2.0 );

Merci beaucoup de votre aide, apparemment tout marche... J'ai même réussi a interdire les cibles dans un "cône" sous la catapulte (qui est définit par la fonction y = 0.1x²)

Il ne me reste qu'a optimiser le script car j'utilisais pas mal de fonctions de type GetFirstObjectInShape(...).

si ça vous intéresse, voici la partie permettant de chercher une cible et de vérifier si elle est bien dans un cône devant la catapulte:
Code PHP:

            int n 0;
            
            
vector vPosCata GetPosition(OBJECT_SELF);
            
float fFacingCata GetFacing(OBJECT_SELF)+180;
            if(
fFacingCata >= 360.0){fFacingCata fFacingCata-360.0;}
            
vector vPosCataRepCata;    
            
            
float fDistancePC;
            
vector vPosPC;
            
vector vPosPCRepCata;
            
float fAnglePC;
            
            
float fAngleConeAvant;
            
float fAngleConeDessous;
            
            while(!
GetIsObjectValid(oCible))//Tant que la cible n'a pas été trouvée
                
{
                
n++;
                
oPC GetNearestCreature(CREATURE_TYPE_IS_ALIVECREATURE_ALIVE_TRUEOBJECT_SELFnCREATURE_TYPE_REPUTATIONREPUTATION_TYPE_ENEMY);
                
                
//AssignCommand(oPC, ActionSpeakString("oPC "+IntToString(n), 2));
                //ActionSpeakString("==> oPC "+IntToString(n)+" :" , 2);
                
                //Si oPC est trop loin, on arrête la boucle
                
if(fDistancePC>=fPorteeMax || !GetIsObjectValid(oPC) )
                    {break;}
                
                
object oJournal GetItemPossessedBy(oPC"journalNODROP");
                
//si oPC est trop près ou est abrité, on prend une autre oPC plus loin
                
fDistancePC GetDistanceToObject(oPC);                
                if(
fDistancePC <= fPorteeMin                        //Il faut que la cible soit hostile par rapport a la faction de la catapulte
                    
|| GetLocalInt(oJournal"nAbriteCatapultes"))    //ou si oPC est abrité des tirs
                    
{continue;}

                
vPosPC GetPosition(oPC);
                    
                
            
//====> Il faut que la cible soit dans un cône de 60° devant la catapulte
                
                //Changement de repère (catapulte = origine)
                
vPosPCRepCata vPosPC vPosCata;
                
vPosCataRepCata Vector(0.00.00.0);
                
                
//oPC rapporté sur le plan horizontal doit etre dans un cône devant la cata
                //On chope l'angle sur le plan (x, y) 
                
vPosPCRepCata.0.0;
                
                
//L'angle entre le facing de la cata et la ligne Catapulte-PC
                
fAnglePC fabs(GetAnglevPosCataRepCatavPosPCRepCatafDistancePC ));
                
fAngleConeAvant fabsfFacingCata fAnglePC );
                                                    
                
//ActionSpeakString("fAngleConeAvant = "+FloatToString(fAngleConeAvant), 2);
                
                 
if(fAngleConeAvant >= 30.0)    //Si l'angle du cône devant est est sup à 30° on change de oPC
                     
{continue;} 
Une dernière question pour la route :
Je me suis retrouvé presque forcé d'utiliser la fonction "LineOfSightVector(vPosCata, vPosPC)" pour vérifier que la cible n'est pas cachée derrière un mur/relief. Dans le toolset il est indiqué que la fonction est très gourmande...
Y a t'il moyen de faire autrement pour vérifier si la cible est visible ou non, d'une manière plus économique bien sûr

encore merci !
Répondre

Connectés sur ce fil

 
1 connecté (0 membre et 1 invité) Afficher la liste détaillée des connectés