Compter le nombre d'appui sur une touche durant un temps donné

Vous trouverez ici les tutoriels et l'entraide sur les outils et techniques de modding spécifiques à Fallout New Vegas.
machiavélique
Testeur aspirant | Moddeur aspirant
Testeur aspirant | Moddeur aspirant
Messages : 24

Compter le nombre d'appui sur une touche durant un temps donné

Message par machiavélique »

Bonjour,

Existe-t-il une méthode simple et pas trop lourde pour compter le nombre d’appui sur une touche durant un temps donné ?
Genre... "Vous avez 1 seconde pour taper deux fois sur 'Q' " ou ... "Vous avez 5 secondes pour taper 'GHT1CDARV' "

Ça se fait ça ou pas ?

Code : Tout sélectionner

scn ouCpasN

int elligent



begin GameMode
	
	if (IsKeyPressed 16 == 1)
		set elligent to elligent + 1
		MessageEx "%g" elligent
	endif

end 

Le problème de ce script est qu'il faut assurer un Script Processing Delay très faible pour capturer la touche. De plus qu'il faut tester si la touche a été relâchée avant d'être rappuyée donc il faut au moins deux exécutions du bloc pour valider un appui sur la touche désirée (ça me semble un peu bourrin comme façon de faire).


Au départ j'avais voulu "rester enfermé" tant le bloc pour... pour faire ce que j'avais à y faire mais ça fait crasher le jeu (même avec un Script Processing Delay élevé, j'avais mis 60 pour être sûr qu'il soit terminé avant d'être ré-exécuté) :

Code : Tout sélectionner

scn ouCpasN

float Timeurt
float TimeurtDenvieDeMaider


begin GameMode

	set Timeurt to GetSecondsPassed
	set TimeurtDenvieDeMaider to GetSecondsPassed

	while ( (Timeurt - TimeurtDenvieDeMaider) < 5 )
	
		; [Code de la détection d'appui de la touche à ajouter]
		set Timeurt to GetSecondsPassed
	
	loop

	MessageEx "Test OK, TiPasMort..."
	
end 


L'idée c'est vraiment de réussir à capturer plusieurs appuis d'une touche sur un temps donné (de l'ordre d'une dizaine de seconde), mais je ne vois pas comment faire ça efficacement. La détection de l'appui c'est gérable, le faire sur un temps imparti ça me semble être une tout autre histoire.
Avatar du membre
Nerapharu
Traducteur aguerri
Traducteur aguerri
Messages : 417

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par Nerapharu »

Hello,

Je ne suis pas encore familier avec Papyrus néanmoins dans ton deuxième script ça me semble bizarre que tu initialises 2 variables sur la même valeur, puis que tu veuilles les soustraire en espérant qu'elles soient inférieures à 5. Mais je n'ai peut-être pas bien saisi.
Avec l'aide de ChatGPT ( :embaras: ) il y a peut-être moyen d'y voir plus clair. D'après lui il faudrait faire quelque chose du genre :

Code : Tout sélectionner

scn CountKeyPresses

float Timer
float EndTime
int KeypressCount

begin GameMode

	; Définir la durée de la période de comptage des pressions
	set Timer to GetSecondsPassed
	set EndTime to Timer + 5.0
	
	; Boucle de comptage des pressions de touche
	while (Timer < EndTime)
	
		; Vérifier si la touche est pressée
		if (IsKeyPressed <code de la touche>)
			; Incrémenter le compteur de pressions de touche
			set KeypressCount to KeypressCount + 1
		endif
		
		; Mettre à jour le minuteur
		set Timer to GetSecondsPassed
		
	loop

	; Afficher le résultat du comptage de pressions de touche
	MessageEx "Nombre de pressions de touche : " + KeypressCount

end
Explication de ChatGPT :

- La première ligne du script crée une nouvelle scène (scn) appelée "CountKeyPresses".
- Les variables "Timer" et "EndTime" sont définies pour suivre le temps écoulé et la fin de la période de comptage, respectivement.
- La variable "KeypressCount" est initialisée à zéro et sera utilisée pour stocker le nombre de fois que la touche est pressée pendant la période de comptage.
- Dans la boucle while, le script vérifie si la touche est pressée. Si c'est le cas, le compteur de pressions de touche est incrémenté de 1.
- Le minuteur est mis à jour à chaque itération de la boucle en utilisant la fonction GetSecondsPassed.
- Lorsque le temps de comptage est écoulé, le résultat du comptage de pressions de touche est affiché à l'aide de la fonction MessageEx.

Notez que vous devrez remplacer <code de la touche> par le code de la touche que vous souhaitez surveiller. Les codes de touche Skyrim peuvent être trouvés sur la page de la communauté Skyrim sur la commande "IsKeyPressed".
machiavélique
Testeur aspirant | Moddeur aspirant
Testeur aspirant | Moddeur aspirant
Messages : 24

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par machiavélique »

Je ne m'attendais pas à une réponse aussi rapide :)
Merci d'essayer de m'aider en tous cas.

J'ai testé ce code tel quel, je m'attendais à ce que ça plante et...
... et ça a planté ! :mrgreen:

J'ai donc repris le code que tu m'as filé en en inhibant différentes parties pour trouver ce qui cause problème et la conclusion est que le strict nécessaire pour le faire planter c'est ça :

Code : Tout sélectionner

scn CountKeyPresses

float Timer
float EndTime
;int KeypressCount

begin GameMode

	; Définir la durée de la période de comptage des pressions
	set Timer to GetSecondsPassed
	set EndTime to Timer + 5.0
	
	; Boucle de comptage des pressions de touche
	while (Timer < EndTime)
	
		; Vérifier si la touche est pressée
		;if (IsKeyPressed 16)
			; Incrémenter le compteur de pressions de touche
			;set KeypressCount to KeypressCount + 1
		;endif
		
		; Mettre à jour le minuteur
		set Timer to GetSecondsPassed
		
	loop

	; Afficher le résultat du comptage de pressions de touche
	;MessageEx "Nombre de pressions de touche : " + KeypressCount

end
soit pour y voir plus clair...

Code : Tout sélectionner

scn CountKeyPresses

float Timer
float EndTime

begin GameMode

	set Timer to GetSecondsPassed
	set EndTime to Timer + 5.0
	
	while (Timer < EndTime)
	
		set Timer to GetSecondsPassed

	loop

end
J'ai mis un Script Processing delay de 20 de sorte à être large.
Car si je mets un Script Processing delay inférieur à 5, je me dis que le bloc va être de nouveau appelé avant que la boucle while ne se termine. Or si le bloc est de nouveau appelé les timers "Timer" et "EndTime" vont à nouveau être réinitialisés, on va de nouveau rentrer dans le while, qui de nouveau ne pourra pas se poursuivre jusqu'à la fin car la fréquence d’exécution du script sera plus élevée que le timer qu'on essaie de mettre en place, et là je pourrais comprendre que le jeu se fige.

Donc je me dis soit il prend pas en compte le Sript Processing Delay que je lui donne (peu probable), soit le GetSecondsPassed ne s'incrémente pas tant que le bloc n'est pas achevé (plus probable, et à dire vrai c'est même forcément ça => https://geckwiki.com/index.php/GetSecondsPassed)

Le grand Geckwiki a écrit :GetSecondsPassed

Description

Returns the number of real life seconds that have passed since this function was last called by the calling script.

L'exemple qu'ils donnent est très clair :

Code : Tout sélectionner

float timer

begin gamemode
   if timer < 5
      set timer to timer + GetSecondsPassed
   else
      ;5 seconds have passed, do something special
      set timer to 0
   endif
end
Le bloc est appelé en boucle, à chaque appel le timer se met à jour car GetSecondsPassed compte le nombre de seconde passées entre chaque appel. Ça explique pourquoi le jeu se fige quand cette fonction est utilisé dans un while => GetSecondsPassed ne peut pas évoluer tant que le bloc n'a pas été terminé (ou interrompu).
Ça m'embête de devoir le faire de cette façon, mais on dirait bien que je n'ai pas le choix. :z:
Avatar du membre
Nerapharu
Traducteur aguerri
Traducteur aguerri
Messages : 417

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par Nerapharu »

Effectivement je me dis que la boucle while n'est peut-être pas nécessaire. J'ai l'intuition qu'un if dans un if pourrait suffire, et je ne pense pas qu'il y ait besoin d'un delay. Après je connais pas bien ce langage ni sa syntaxe --' J'essaierais peut-être ça :

Code : Tout sélectionner

float Timer
float EndTime
int KeypressCount = 0

begin GameMode	
	set Timer to GetSecondsPassed
	set EndTime to Timer + 5.0
	
	if Timer < EndTime		
		if (IsKeyPressed 16)			
			set KeypressCount to KeypressCount + 1
		endif		
		set Timer to GetSecondsPassed
	else	
               ;5 secondes sont passées -> définir ce qu'il se passe.                             
       endif
end
Par contre dans le else faut-il vraiment remettre le Timer à 0 comme dans ton dernier exemple ? Ca va rappeler la boucle à l'infini, ce qui n'est peut-être pas ce que tu veux :pensif:

Et oui de ce que je comprends le GetSecondsPassed est le "timer référentiel" qui s'incrémente en toute circonstance. En revanche la variable Timer qu'on initialise avec la valeur de GetSecondsPassed, elle, est figée, d'où la nécessité de refaire un set Timer to GetSecondsPassed dans la boucle -> pour actualiser le timing et vérifier si 5 secondes se sont passées depuis l'initialisation.
Modifié en dernier par Nerapharu le 06 avr. 2023, 09:35, modifié 1 fois.
machiavélique
Testeur aspirant | Moddeur aspirant
Testeur aspirant | Moddeur aspirant
Messages : 24

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par machiavélique »

Je ne pense pas que ça marchera à cause des timers qui se réinitialisent à chaque fois que le script se lance.

Je ne pense pas qu'il y ait besoin d'initialiser les timers (ils ne le font pas dans leur exemple), à mon avis si une variable n'est pas initialisée je pense que le compilateur doit lui attribuer la valeur "0" par défaut. Ça peut se vérifier très facilement, il suffit d'aller checker en jeu la valeur d'une variable déclarée mais non initialisée et de voir si elle contient "0" ou "n'importe quoi".

Ce qui m'embête c'est que je ne suis pas certain de pouvoir "garantir" le délai de traitement d'un bloc GameMode/end.
Même si je lui dis que je veux un délai de traitement de 0.05 seconde avec une priorité assez élevée, en plus d'être gourmand en ressources que se passera-t-il si le jeu, ainsi qu'un potentiel grand nombre de mods joueur, doivent également lancés de leur côté tout une floppée d'autres scripts ?

J'ai peut-être une autre piste à étudier avec les events, ça éviterait de créer un script qui serait gourmand en ressources pour au final pas grand-chose.

Nerapharu a écrit : 06 avr. 2023, 08:25 En revanche la variable Timer qu'on initialise avec la valeur de GetSecondsPassed, elle, est figée, d'où la nécessité de refaire un set Timer to GetSecondsPassed dans la boucle -> pour actualiser le timing et vérifier si 5 secondes se sont passées depuis l'initialisation.
Elle est figée tant que le script n'est pas réexecuté, ce qui se produira automatiquement au bout du "Script Processing delay" (5 secondes par défaut si on ne lui spécifie pas de valeur) et ce, tant que le script est actif. Donc admettons qu'on ne mette pas de valeur alors toutes les 5 secondes (sûrement "environ 5 secondes" car c'est pas le seul script à devoir être lancé et je ne sais pas comment le moteur de FNV gère tout ça), ce script sera réexecuté et la valeur de la variable Timer réécrasée par la nouvelle valeur de GetSeconsPassed.
Avatar du membre
Nerapharu
Traducteur aguerri
Traducteur aguerri
Messages : 417

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par Nerapharu »

Je ne suis pas sûr de comprendre le Script Processing delay, tu le positionnes où ? Il sert à quoi exactement ?
machiavélique
Testeur aspirant | Moddeur aspirant
Testeur aspirant | Moddeur aspirant
Messages : 24

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par machiavélique »

Dans les quêtes :
Image


Si ici on prends une quête du jeu au hasard (ex VUltraLuxeSecurity) et qu'on regarde ce qu'il y a dessus
Image
On remarque qu'il y a un script attaché à cette quête (VUltraLuxeSecuritySCRIPT), cette quête n'est pas active par défaut et doit être activée par quelque chose pour que le script démarre (un autre script par exemple).
Lorsqu'elle sera active, le script qui lui est associé se lancera toutes les 10 secondes (configuré via le Script Processing Delay, si la case est cochée il ignorera la valeur dans la case "Default" et sera configuré sur 5 secondes).

Donc ensuite si on jette un œil au script :
Image
Toutes les 10 secondes il exécutera le code contenu dans le script depuis le début. C'est comme si le script était lui-même imbriqué dans une boucle "While (1)" avec tous les autres script du jeu. Quand le moteur du jeu arrive sur le script, qu'il voit que le délai du "Script Processing Delay" a expiré, il exécute le script et réinitialise la valeur du "Script Processing Delay" pour une prochaine itération.


Je ne sais pas exactement comment fonctionne le moteur de jeu, mais j'ai l'impression qu'on peut le voir de la manière suivante (c'est sûrement pas écrit comme ça dans le moteur de jeu mais c'est juste pour donner une vague idée de comment ça semble fonctionner) :

Code : Tout sélectionner

while (1){

   if (IDQuest1.isActive AND IDQuest1.ScriptProcessingDelayElapsed)
      RunScript IDQuest1.Script
   endif   

   if (IDQuest2.isActive AND IDQuest2.ScriptProcessingDelayElapsed)
      RunScript IDQuest2.Script
   endif   

   if (IDQuest3.isActive AND IDQuest3.ScriptProcessingDelayElapsed)
      RunScript IDQuest3.Script
   endif   

   if (IDQuest4.isActive AND IDQuest4.ScriptProcessingDelayElapsed)
      RunScript IDQuest4.Script
   endif   

   ; etc etc
   ; .
   ; .
   ; .
   ; .
   ; .
   ; .
   ; .
   ; .

   if (IDQuest3690.isActive AND IDQuest3690.ScriptProcessingDelayElapsed)
      RunScript IDQuest3690.Script
   endif   

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Le script en question ;;;;;;;;;;;;;;;;;;;;;;
;  if (VUltraLuxeSecurity.isActive AND VUltraLuxeSecurity.ScriptProcessingDelayElapsed)                           ;
;     RunScript VUltraLuxeSecuritySCRIPT                                                                          ;
;  endif                                                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

   if (IDQuest3692.isActive AND IDQuest3692.ScriptProcessingDelayElapsed)
      RunScript IDQuest3692.Script
   endif 
   
   ; .
   ; .
   ; .
   ; .
   ; .

}
C'est évidemment n'importe quoi ce que j'ai écrit au dessus, mais c'est juste pour dire que derrière le script il y a un traitement qui se fait avec des délais de traitement, des priorités, des interruptions...

Plus il y a de scripts avec des fréquences de réitération élevée et plus ça bouffe en ressource. Non seulement ça risque de faire "galérer" le jeu sur des petites machines mais en plus s'il y a pleins de scripts à gérer au moment de l'appui de la touche je risque de passer à côté (c.-à-d. que le joueur appuie et relâche la touche sans que mon script ait pu être exécuté par le moteur de jeu à ce moment-là.
Si j'arrive à passer par les events, j'y gagnerai sur tous les terrains (performance et efficacité).

Le grand Geckwiki a écrit :SetEventHandler

Description

Registers a User-Defined Function (UDF) as an Event Handler for the specified event.

This means that whenever the specified event occurs in-game, if a UDF was set as an Event Handler for that event, the UDF's script will run, allowing for instant reactions to certain events.
Avatar du membre
Nerapharu
Traducteur aguerri
Traducteur aguerri
Messages : 417

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par Nerapharu »

Haa merci pour l'explication. Du coup je comprends mieux. Si y avait moyen de mettre le comptage des appuis de touche dans une boucle while comme tu voulais le faire, celle-ci devrait s'exécuter entièrement avant que Script Processing delay réinitialise tout. Mais du coup ça me paraît incompréhensible qu'un simple
"
while (Timer < EndTime)
set Timer to GetSecondsPassed
loop
"
fasse bugguer ton jeu. Je crains de ne pas pouvoir t'aider :pensif:
Après ce n'est qu'une supposition, mais je me dis que le script processing delay s'enclenche probablement toutes les x secondes à partir du moment où les scripts rattachés sont finis d'être lus, et non pendant qu'ils sont lus. Ca me paraîtrait infernal à gérer sinon.
machiavélique
Testeur aspirant | Moddeur aspirant
Testeur aspirant | Moddeur aspirant
Messages : 24

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par machiavélique »

Le

"
while (Timer < EndTime)
set Timer to GetSecondsPassed
loop
"

fait freezer le jeu car GetSecondsPassed n'évolue pas. GetSecondsPassed est la différence de temps en seconde qui s'est écoulée entre les deux appels du script par le moteur du jeu, or si le script est bloqué dans un while (1), il ne peut pas se terminer et GetSecondsPassed évoluer au prochain appel du script. Le jeu se fige car le système est bloqué dans une boucle infinie dans laquelle il ne peut sortir. Plus aucun autre script se lance car le moteur qui gère ça est juste simplement occupé à réécrire inlassablement et en permanence la même valeur (celle de GetSecondPassed) dans la variable Timer => Le jeu est figé mais actif, le moteur ne plante pas => Il est simplement occupé à faire bêtement ce qu'on lui demande : Réécrire la même valeur dans une variable...

Pour sortir de la boucle il faut que GetSecondsPassed évolue, mais pour que GetSecondsPassed évolue il faut sortir de la boucle... c'est juste la fameuse histoire du Gecko qui se mord la queue...
Avatar du membre
Nerapharu
Traducteur aguerri
Traducteur aguerri
Messages : 417

Re: Compter le nombre d'appui sur une touche durant un temps donné

Message par Nerapharu »

Ah ok je vois, c'est quoi ces fonctions mal fichues ! :D
En tout cas si tu trouves une solution à ton problème, hésite pas à la partager, ça peut être utile.

Lorsque j'ai interrogé chatGPT sur comment faire un tel script, il m'a aussi donné une version en relation avec des events. C'est à adapter mais je te la partage au cas où :
► Afficher le texte
Répondre

Retourner vers « Modder Fallout New Vegas »