Dans la continuité de la dernière fois, il s'agit d'un article dans lequel les débutants continueront à travailler à la création d'une bibliothèque Python qui implique UE4 (presque comme un mémorandum pour moi-même ...).
Premièrement: # 1 Dernière fois: # 4
Dans l'article précédent, nous avons connecté la fonction de rotation immédiate avec Python, mais ensuite nous connecterons également celle avec animation.
Commençons par le travail du côté des plans. C'est presque le même que le BP de rotation immédiate. Presque tout ce dont vous avez besoin est prêt, il vous suffit donc de faire une boucle autour du cube et d'indiquer la rotation du cube que vous souhaitez faire pivoter.
Tourne la boucle,
Vérifiez s'il s'agit d'une cible de rotation,
Définissez un drapeau.
Préparez une fonction de plan pour la rotation dans une direction et essayez de l'appeler depuis Python avec du code provisoire.
Content\Scripts\action.py
...
class Action:
total_delta_time = 0
rotation_count = 0
def tick(self, delta_time):
"""
Une fonction qui est exécutée à peu près toutes les images pendant le jeu.
Parameters
----------
delta_time : float
Secondes écoulées depuis le dernier appel de tick.
"""
self.total_delta_time += delta_time
if self.total_delta_time > 3 and self.rotation_count == 0:
self.uobject.rotateXLeft1()
self.rotation_count += 1
return
if self.total_delta_time > 6 and self.rotation_count == 1:
self.uobject.rotateXLeft2()
self.rotation_count += 1
return
if self.total_delta_time > 9 and self.rotation_count == 2:
self.uobject.rotateXLeft3()
self.rotation_count += 1
return
if self.total_delta_time > 12 and self.rotation_count == 3:
self.uobject.rotateXRight1()
self.rotation_count += 1
return
...
J'ai vérifié la rotation et il semble qu'il n'y ait pas de problème, je vais donc passer à la suivante. De plus, en fonction de l'objectif de rotation, l'affichage peut être cassé ou rugueux, mais cela sera-t-il corrigé par l'emballage pour la distribution? Ou y a-t-il quelque chose qui manque ...? (Anti-aliasé)
Lors de son utilisation réelle, il est nécessaire d'enregistrer la bibliothèque avec PyPI (pip) etc., d'appeler ces bibliothèques depuis Jupyter etc. et de donner une instruction d'action du côté UE4.
Par conséquent, nous préparerons une table sur laquelle les données seront écrites du côté Python de la bibliothèque. J'ai pensé que je continuerais avec le module via PyActor, mais j'ai trouvé quelques inconvénients, donc je vais l'écrire avec un module généraliste sous commun.
Win64\common\sqlite_utils.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
...
declarative_meta = declarative_base()
...
#Le statut de l'état ajouté à la cible de traitement de l'action.
ACTION_INSTRUCTION_STATUS_QUEUED = 1
#Le statut de l'état en cours (animation) de l'action.
ACTION_INSTRUCTION_STATUS_RUNNING = 2
#Le statut de l'état traité de l'action.
ACTION_INSTRUCTION_STATUS_ENDED = 3
class ActionInstruction(declarative_meta):
"""
Gère les données d'instruction d'action du côté de la bibliothèque Python
Modèle de table.
Attributes
----------
id : Column of Integer
Colonne de clé primaire.
action : Column of Integer
L'action spécifiée. La définition est action.Selon la valeur en py.
current_status : Column of Integer
Le statut de l'action en cours. Dans ce module
ACTION_INSTRUCTION_STATUS_D'une constante avec un préfixe de
Suivez la définition.
skip_animation : Column of Integer
Spécifiez s'il faut ignorer l'animation.
0=Ne sautez pas, 1=Il est prêt à sauter.
"""
id = Column(Integer, primary_key=True)
action = Column(Integer)
current_status = Column(Integer)
skip_animation = Column(Integer)
__tablename__ = 'action_instruction'
session = None
def create_from_python_db_session():
"""
Session de lecture de SQLite écrite à partir de la bibliothèque Python
Produire.
Returns
-------
session : Session
La session SQLite générée.
Notes
-----
Si la session a déjà été créée, l'instance créée sera renvoyée.
"""
global session
if session is not None:
return session
session_start_time_str = file_helper.get_session_start_time_str()
file_name = 'from_python_%s.sqlite' % session_start_time_str
session = create_session(
sqlite_file_name=file_name,
declarative_meta=declarative_meta)
return session
Pour le moment, j'ajouterai un test pour confirmer qu'une session peut être créée et une ligne d'insertion et de suppression réussit.
Win64\common\tests\test_sqlite_utils.py
def test_create_from_python_db_session():
session = sqlite_utils.create_from_python_db_session()
action_instruction = sqlite_utils.ActionInstruction()
action_instruction.action = 1
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_QUEUED
action_instruction.skip_animation = 0
session.add(instance=action_instruction)
session.commit()
action_instruction = session.query(
sqlite_utils.ActionInstruction
).filter_by(
action=1,
current_status=sqlite_utils.ACTION_INSTRUCTION_STATUS_QUEUED,
skip_animation=0).one()
session.delete(action_instruction)
session.commit()
Ensuite, du côté action.py, écrivez un processus pour vérifier périodiquement les valeurs de la table préparée. Avant cela, si vous traitez avec tick, le processus sera exécuté assez fréquemment et il sera inutile (l'accès à la base de données, etc. sera exécuté fréquemment), donc réduisez la fréquence d'exécution. Essayez de le régler une fois toutes les 0,2 secondes environ (ajustez-le en observant la situation).
Il semble que cela puisse être géré en ouvrant BP_Action et en ajustant l'intervalle de ticks (secs). La valeur par défaut est 0, et si elle est 0, la fréquence d'exécution sera basée sur la fréquence d'images.
Essayez de spécifier la sortie de la console côté Python et vérifiez-la pour le moment.
Content\Scripts\action.py
class Action:
...
def tick(self, delta_time):
"""
Une fonction qui est exécutée à intervalles réguliers pendant le jeu.
Parameters
----------
delta_time : float
Secondes écoulées depuis le dernier appel de tick.
"""
ue.log(delta_time)
Il s'exécute toutes les 0,2 secondes. Ça me va.
Ajoutez un processus pour examiner les données SQLite à intervalles réguliers et récupérer une donnée d'action si elle existe. Assurez-vous que None est renvoyé lorsqu'une autre action tourne ou qu'il n'y a pas de données pour l'action spécifiée.
Content\Scripts\action.py
class Action:
def begin_play(self):
"""Une fonction qui est exécutée au début du jeu.
"""
self.from_python_db_session = \
sqlite_utils.create_from_python_db_session()
python_test_runner.run_pyactor_instance_tests(
pyactor_class_instance=self)
def tick(self, delta_time):
"""
Une fonction qui est exécutée à intervalles réguliers pendant le jeu.
Parameters
----------
delta_time : float
Secondes écoulées depuis le dernier appel de tick.
"""
action, skip_animation = self._get_action_instruction_data()
if action is None:
return
pass
def _get_action_instruction_data(self):
"""
Acquiert les informations d'action définies. L'état dans lequel l'action suivante peut être exécutée
(L'animation de rotation est terminée, etc.) et l'action suivante est spécifiée
S'il existe, la valeur de la première action non traitée est renvoyée.
Returns
----------
action : int or None
La valeur de type de l'action récupérée. ACTION dans ce module_
Une valeur avec le préfixe de est définie. Lorsque la cible n'existe pas
Aucun n'est défini si l'action suivante ne peut pas être exécutée.
skip_animation : bool or None
Définir s'il faut ignorer l'animation. La cible n'existe pas
Aucun est défini si la condition est telle que l'action suivante ne peut pas être exécutée.
"""
if self.is_any_cube_rotating():
return None, None
query_result = self.from_python_db_session.query(
ActionInstruction
).filter_by(
current_status=sqlite_utils.ACTION_INSTRUCTION_STATUS_QUEUED
).order_by(
ActionInstruction.id.asc()
).limit(1)
for action_instruction in query_result:
action = int(action_instruction.action)
skip_animation = int(action_instruction.skip_animation)
if skip_animation == 0:
skip_animation = False
else:
skip_animation = True
return action, skip_animation
return None, None
def is_any_cube_rotating(self):
"""
Obtient la valeur booléenne indiquant si un cube est en train de tourner.
Returns
----------
is_rotating : bool
True est défini si un cube tourne.
"""
is_rotating = self.uobject.isAnyCubeRotating()[0]
return is_rotating
def test__get_action_instruction_data(self):
#L'action spécifiée existe et l'animation
#Vérifiez le comportement lorsque le non-saut est spécifié.
action_instruction = sqlite_utils.ActionInstruction()
action_instruction.action = ACTION_ROTATE_X_LEFT_2
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_QUEUED
action_instruction.skip_animation = 0
self.from_python_db_session.add(instance=action_instruction)
self.from_python_db_session.commit()
action, skip_animation = self._get_action_instruction_data()
assert_equal(
action,
ACTION_ROTATE_X_LEFT_2)
assert_false(skip_animation)
self.from_python_db_session.delete(action_instruction)
self.from_python_db_session.commit()
#Vérifiez le comportement lorsque l'animation est ignorée.
action_instruction = sqlite_utils.ActionInstruction()
action_instruction.action = ACTION_ROTATE_X_LEFT_2
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_QUEUED
action_instruction.skip_animation = 1
self.from_python_db_session.add(instance=action_instruction)
self.from_python_db_session.commit()
_, skip_animation = self._get_action_instruction_data()
assert_true(skip_animation)
self.from_python_db_session.delete(action_instruction)
self.from_python_db_session.commit()
#Vérifiez le comportement lorsque les données spécifiées n'existent pas.
action, skip_animation = self._get_action_instruction_data()
assert_equal(action, None)
assert_equal(skip_animation, None)
def test_is_any_cube_rotating(self):
assert_false(
self.is_any_cube_rotating())
Pour exécuter le test de la classe spécifiée par PyActor, ajoutez une fonction pour exécuter la méthode de test de la classe cible séparément au module polyvalent, et exécutez-la au moment de begin_play (en fait, c'est généralement normal). Ce serait bien si la fonction à tester s'écoule automatiquement comme pytest dans pytest, mais c'est parce que la classe est instanciée depuis UE4). Comme il est difficile d'écrire tous les modèles de test car il est connecté à UE4, j'écrirai des tests dans la mesure où je peux écrire rapidement.
Win64\common\python_test_runner.py
...
def run_pyactor_instance_tests(pyactor_class_instance):
"""
Défini sur l'instance de la classe Python spécifiée par PyActor
Exécutez un ensemble de fonctions de test.
Parameters
----------
pyactor_class_instance : *
Une instance de la classe Python spécifiée par le PyActor cible.
"""
print('%Commencer à tester s...' % type(pyactor_class_instance))
is_packaged_for_distribution = \
file_helper.get_packaged_for_distribution_bool()
if is_packaged_for_distribution:
return
members = inspect.getmembers(pyactor_class_instance)
for member_name, member_val in members:
if not inspect.ismethod(member_val):
continue
if not member_name.startswith('test_'):
continue
print('%Fonction cible: %s' % (datetime.now(), member_name))
pre_dt = datetime.now()
member_val()
timedelta = datetime.now() - pre_dt
print('%s ok. %s secondes' % (datetime.now(), timedelta.total_seconds()))
Maintenant que vous pouvez spécifier l'action, j'écrirai l'endroit pour appeler la fonction côté blueprint lié à la valeur de l'action acquise.
Content\Scripts\action.py
...
ACTION_KEY_FUNC_NAME_DICT = {
ACTION_ROTATE_X_LEFT_1: 'rotateXLeft1',
ACTION_ROTATE_X_LEFT_2: 'rotateXLeft2',
ACTION_ROTATE_X_LEFT_3: 'rotateXLeft3',
ACTION_ROTATE_X_RIGHT_1: 'rotateXRight1',
ACTION_ROTATE_X_RIGHT_2: 'rotateXRight2',
ACTION_ROTATE_X_RIGHT_3: 'rotateXRight3',
ACTION_ROTATE_Y_UP_1: 'rotateYUp1',
ACTION_ROTATE_Y_UP_2: 'rotateYUp2',
ACTION_ROTATE_Y_UP_3: 'rotateYUp3',
ACTION_ROTATE_Y_DOWN_1: 'rotateYDown1',
ACTION_ROTATE_Y_DOWN_2: 'rotateYDown2',
ACTION_ROTATE_Y_DOWN_3: 'rotateYDown3',
ACTION_ROTATE_Z_UP_1: 'rotateZUp1',
ACTION_ROTATE_Z_UP_2: 'rotateZUp2',
ACTION_ROTATE_Z_UP_3: 'rotateZUp3',
ACTION_ROTATE_Z_DOWN_1: 'rotateZDown1',
ACTION_ROTATE_Z_DOWN_2: 'rotateZDown2',
ACTION_ROTATE_Z_DOWN_3: 'rotateZDown3',
}
...
class Action:
...
def tick(self, delta_time):
"""
Une fonction qui est exécutée à intervalles réguliers pendant le jeu.
Parameters
----------
delta_time : float
Secondes écoulées depuis le dernier appel de tick.
"""
action, skip_animation = self._get_action_instruction_data()
if action is None:
return
action_func_name: str = self._get_action_func_name(
action=action,
skip_animation=skip_animation,
)
action_func = getattr(self.uobject, action_func_name)
action_func()
def _get_action_func_name(self, action, skip_animation):
"""
À partir de la valeur de type de l'action spécifiée, etc., du plan directeur cible
Obtenez le nom de la fonction.
Parameters
----------
action : int
La valeur de type de l'action cible.
skip_animation : bool
S'il faut ignorer l'animation.
Returns
----------
func_name : str
Le nom de la fonction calculée.
"""
func_name: str = ACTION_KEY_FUNC_NAME_DICT[action]
if skip_animation:
last_char = func_name[-1]
func_name = func_name[0:-1]
func_name += 'Immediately%s' % last_char
return func_name
...
def test__get_action_func_name(self):
func_name = self._get_action_func_name(
action=ACTION_ROTATE_X_LEFT_2,
skip_animation=False)
assert_equal(func_name, 'rotateXLeft2')
func_name = self._get_action_func_name(
action=ACTION_ROTATE_X_LEFT_2,
skip_animation=True)
assert_equal(func_name, 'rotateXLeftImmediately2')
Nous avons préparé un dictionnaire avec le nom de la fonction en fonction de la valeur du type d'action, et obtenu le nom de la fonction en s'y référant. S'il est paramétré pour ignorer l'animation, il correspond en ajoutant le suffixe du nom de la fonction pour une rotation immédiate.
Si cela vous convient, vous devriez pouvoir effectuer une rotation du côté UE4 en écrivant des données dans la table SQLite. Essayez d'ajouter manuellement des enregistrements à SQLite.
Il semble qu'il tourne comme spécifié par SQLite comme prévu. J'ai également défini les valeurs de type d'autres actions et vérifié la rotation, mais cela ne semble pas poser de problème.
Je me demandais si je devais séparer les fichiers selon le sens d'écriture entre Python en UE4 et Python dans la bibliothèque, mais j'ai commencé à sentir que c'était inutile car il devenait nécessaire de s'écrire entre eux. Essayez de combiner les fichiers en un seul et ajustez le nom du fichier et le nom de la variable (les détails sont omis).
Actuellement, la valeur de current_status de SQLite n'est pas mise à jour, donc la rotation est répétée, donc ajustez cette zone.
Ajoutez principalement le traitement des trois fonctions suivantes.
Content\Scripts\action.py
...
def tick(self, delta_time):
"""
Une fonction qui est exécutée à intervalles réguliers pendant le jeu.
Parameters
----------
delta_time : float
Secondes écoulées depuis le dernier appel de tick.
"""
self._set_ended_status_to_animation_ended_action()
action_instruction_id, action, skip_animation = \
self._get_action_instruction_data()
if action_instruction_id is None:
return
action_func_name: str = self._get_action_func_name(
action=action,
skip_animation=skip_animation,
)
action_func = getattr(self.uobject, action_func_name)
action_func()
self._set_running_status(
action_instruction_id=action_instruction_id,
skip_animation=skip_animation)
self._set_ended_status_if_animation_skipped(
action_instruction_id=action_instruction_id,
skip_animation=skip_animation)
def _set_ended_status_to_animation_ended_action(self):
"""
Si l'animation est terminée, pendant l'animation
Terminé au statut de réglage de l'action définie
(ACTION_INSTRUCTION_STATUS_ENDED) est défini.
Returns
----------
updated : bool
Si le processus de mise à jour a été exécuté.
"""
if self.is_any_cube_rotating():
return False
target_data_exists = False
action_instructions = self.action_data_db_session.query(
sqlite_utils.ActionInstruction
).filter_by(
current_status=sqlite_utils.ACTION_INSTRUCTION_STATUS_RUNNING,
)
action_instruction: sqlite_utils.ActionInstruction
for action_instruction in action_instructions:
target_data_exists = True
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_ENDED
if target_data_exists:
self.action_data_db_session.commit()
return True
return False
def _set_ended_status_if_animation_skipped(
self, action_instruction_id, skip_animation):
"""
Réglage de l'action cible en cas de réglage pour sauter l'animation
Terminé au statut de (ACTION_INSTRUCTION_STATUS_FIN)
Ensemble.
Parameters
----------
action_instruction_id : int
ID de la clé primaire du paramètre d'action cible.
skip_animation : bool
S'il faut ignorer l'animation.
Returns
----------
updated : bool
Si le processus de mise à jour a été exécuté.
"""
if not skip_animation:
return False
action_instruction: sqlite_utils.ActionInstruction
action_instruction = self.action_data_db_session.query(
sqlite_utils.ActionInstruction
).filter_by(
id=action_instruction_id
).one()
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_ENDED
self.action_data_db_session.commit()
return True
def _set_running_status(self, action_instruction_id, skip_animation):
"""
Si le paramètre de rotation est animé, le paramètre d'action cible
En cours d'exécution vers l'état (ACTION)_INSTRUCTION_STATUS_FONCTIONNEMENT)
Ensemble.
Parameters
----------
action_instruction_id : int
ID de la clé primaire du paramètre d'action cible.
skip_animation : bool
S'il faut ignorer l'animation.
Returns
----------
updated : bool
Si le processus de mise à jour a été exécuté.
"""
if skip_animation:
return False
action_instruction: sqlite_utils.ActionInstruction
action_instruction = self.action_data_db_session.query(
sqlite_utils.ActionInstruction
).filter_by(
id=action_instruction_id).one()
action_instruction.current_status = \
sqlite_utils.ACTION_INSTRUCTION_STATUS_RUNNING
self.action_data_db_session.commit()
return True
En fait, j'ai mis les valeurs dans la table SQLite et confirmé qu'elle ne tourne qu'une seule fois. Vérifiez également que la valeur de l'état est 2 (pendant l'animation) pendant l'animation et 3 (fin) une fois terminé, et pour le traitement de rotation immédiat.
Maintenant, l'animation ne s'arrête pas ...
Créez les valeurs dans la section «Observation» de Introduction à l'apprentissage amélioré # 1: Termes de base et introduction à Gym, PyTorch. C'est une valeur d'observation pour l'apprentissage.
Quant à quoi faire
--Ajouter une nouvelle table SQLite pour l'observation --Enregistrer la valeur d'observation à la fin de l'action
Nous ajouterons des traitements tels que.
Tout d'abord, ajoutez un modèle.
Win64\common\sqlite_utils.py
class Observation(declarative_meta):
"""
Un modèle de table qui gère les données de valeur observée des résultats d'action.
La valeur est enregistrée à chaque fois que l'action se termine (l'animation se termine).
Attributes
----------
id : Column of Integer
Colonne de clé primaire.
action_instruction_id : int
ID de la clé primaire des informations sur l'action cible (action terminée).
position_num : int
Numéro de position du cube. Il est réglé dans l'ordre pour chaque surface.
color_type : int
La valeur du type de couleur de la position cible.
"""
id = Column(Integer, primary_key=True)
action_instruction_id = Column(Integer, index=True)
position_num = Column(Integer)
color_type = Column(Integer)
__tablename__ = 'observation'
Ensuite, nous ajouterons la valeur du type de position et la définition du type de couleur. Le devant est orange, et il est attribué comme décrit et pressé plus tard dans l'état du réglage de disposition des couleurs standard du monde. Le jeu de couleurs est le même que le jeu de couleurs standard mondial sur Wikipedia.
[Rubik Cube-Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AB%E3%83%BC%E3%83%93%E3%83%83%E3%82%AF% Image citée de E3% 82% AD% E3% 83% A5% E3% 83% BC% E3% 83% 96).
Attribuez de 1 à 9 comme indiqué ci-dessous. Nous utiliserons le mot «FRONT» dans le nom de la constante.
Allouez de 10 à 18 comme indiqué ci-dessous. Nous utiliserons le mot "LEFT" dans le nom de la constante.
Allouez de 19 à 27 comme indiqué ci-dessous. Nous utiliserons le mot "DROITE" dans le nom de la constante.
Allouez entre 28 et 36 comme indiqué ci-dessous. Nous utiliserons le mot «TOP» dans le nom de la constante.
Allouez entre 37 et 45 comme indiqué ci-dessous. Nous utiliserons le mot «RETOUR» dans les noms constants.
Allouez entre 46 et 54 comme indiqué ci-dessous. Nous utiliserons le mot "BOTTOM" dans le nom de la constante.
Définissez-le en Python. Le numéro du nom de la constante est défini par le numéro de ligne et de colonne.
Win64\common\sqlite_utils.py
POSITION_NUM_FRONT_1_1 = 1
POSITION_NUM_FRONT_2_1 = 2
POSITION_NUM_FRONT_3_1 = 3
POSITION_NUM_FRONT_1_2 = 4
POSITION_NUM_FRONT_2_2 = 5
POSITION_NUM_FRONT_3_2 = 6
POSITION_NUM_FRONT_1_3 = 7
POSITION_NUM_FRONT_2_3 = 8
POSITION_NUM_FRONT_3_3 = 9
POSITION_NUM_FRONT_LIST = [
POSITION_NUM_FRONT_1_1,
POSITION_NUM_FRONT_2_1,
POSITION_NUM_FRONT_3_1,
POSITION_NUM_FRONT_1_2,
POSITION_NUM_FRONT_2_2,
POSITION_NUM_FRONT_3_2,
POSITION_NUM_FRONT_1_3,
POSITION_NUM_FRONT_2_3,
POSITION_NUM_FRONT_3_3,
]
POSITION_NUM_LEFT_1_1 = 10
POSITION_NUM_LEFT_2_1 = 11
POSITION_NUM_LEFT_3_1 = 12
...
POSITION_NUM_LIST = [
POSITION_NUM_FRONT_1_1,
POSITION_NUM_FRONT_2_1,
POSITION_NUM_FRONT_3_1,
POSITION_NUM_FRONT_1_2,
POSITION_NUM_FRONT_2_2,
POSITION_NUM_FRONT_3_2,
POSITION_NUM_FRONT_1_3,
POSITION_NUM_FRONT_2_3,
POSITION_NUM_FRONT_3_3,
POSITION_NUM_LEFT_1_1,
POSITION_NUM_LEFT_2_1,
POSITION_NUM_LEFT_3_1,
POSITION_NUM_LEFT_1_2,
...
POSITION_NUM_BOTTOM_1_3,
POSITION_NUM_BOTTOM_2_3,
POSITION_NUM_BOTTOM_3_3,
]
Ceci est simplement défini en 6 couleurs.
Win64\common\sqlite_utils.py
COLOR_TYPE_ORANGE = 1
COLOR_TYPE_WHITE = 2
COLOR_TYPE_YELLOW = 3
COLOR_TYPE_GREEN = 4
COLORR_TYPE_BLUE = 5
COLOR_TYPE_RED = 6
COLOR_TYPE_LIST = [
COLOR_TYPE_ORANGE,
COLOR_TYPE_WHITE,
COLOR_TYPE_YELLOW,
COLOR_TYPE_GREEN,
COLORR_TYPE_BLUE,
COLOR_TYPE_RED,
]
Il n'y a pas si longtemps, le processus de réinitialisation était exécuté directement sur le plan de niveau, mais nous allons le connecter avec Python. Comme les autres fonctions précédentes, nous avons défini chaque fonction dans le plan de niveau, nous allons donc ajouter une bibliothèque de fonctions et la déplacer afin qu'elle puisse être référencée à partir du niveau et BP_Action. (À partir de maintenant, traitons-le correctement dès le début avec la bibliothèque de fonctions ... (auto-conseillé))
En ce qui concerne le traitement, les contenus qui étaient dans le plan de presque le niveau sont conservés tels quels, et seule une partie telle que le processus d'acquisition du cube est ajustée (remplacement par le nœud Obtenir tous les acteurs de la classe, etc., seule la partie qui n'est pas apportée car elle est ajustée).
Essayez de l'exécuter une fois et assurez-vous qu'il est mélangé (à ce stade, vous exécutez les fonctions de la bibliothèque de fonctions à partir du niveau BP).
Il ne semble y avoir aucun endroit où le processus d'affirmation sur chaque BP et le test sur Python sont bloqués, donc cela semble aller.
Ajoutez une interface à BP_Action pour appeler depuis Python.
C'est une fonction simple qui appelle simplement une fonction dans la bibliothèque de fonctions. Nous ajusterons également Python. Ajoutez une définition d'action.
Content\Scripts\action.py
...
ACTION_ROTATE_Z_DOWN_2 = 17
ACTION_ROTATE_Z_DOWN_3 = 18
ACTION_RESET = 19
...
ACTION_LIST = [
...
ACTION_ROTATE_Z_DOWN_2,
ACTION_ROTATE_Z_DOWN_3,
ACTION_RESET,
]
...
ACTION_KEY_FUNC_NAME_DICT = {
...
ACTION_ROTATE_Z_DOWN_2: 'rotateZDown2',
ACTION_ROTATE_Z_DOWN_3: 'rotateZDown3',
ACTION_RESET: 'reset',
}
Essayez de mettre les données de 19 actions directement dans SQLite et vérifiez l'opération.
Le cube a été mélangé correctement. Même si je mets à jour l'affichage des données côté SQLite et que je le vérifie, l'action est à l'état terminé.
Je suis allé ici rapidement. Nous continuerons d'avancer.
Vous pouvez dire où chaque cube est situé par l'implémentation jusqu'à présent, mais il est nécessaire de réfléchir à quel côté vous pouvez voir car la rotation, etc. est impliquée (cette valeur est nécessaire pour l'observation) Sera).
Je pensais qu'il serait calculé à partir de la quantité de rotation, mais comme il est tourné en fonction du standard mondial et tourné encore et encore, il y a de nombreux cas où cela ne devient pas une belle valeur.
Cela semble simple, mais il y a pas mal de signes que le calcul semble gênant ... Que dois-je faire ... J'y réfléchis depuis un moment, mais je pense que je peux le calculer en obtenant la position mondiale des acteurs (plans de chaque couleur) dans le plan de la classe de base du cube, donc je vais l'essayer.
Je voulais obtenir les coordonnées de référence mondiale XYZ du plan en fonction de la valeur de type de chaque position, mais je n'ai pas encore utilisé un tel dictionnaire ou un nœud de tableau associatif. Je vais essayer de savoir à quoi ça ressemble.
Il semble que l'histoire soit un peu différente entre C ++ et Blueprint, mais il semble que Map devrait être utilisé sur Blueprint. Il semble que tous les types ne peuvent pas être lancés, mais il semble qu'ils prennent probablement en charge des types tels que les nombres de base et les chaînes.
As far as I know, not every type of variable can be defined as a Map or Set in Blueprints... Maybe there's no such limitation in c++, but I'm not sure. Dictionary?
Le tableau associatif multidimensionnel ... Comme le tableau multidimensionnel, ne semble-t-il pas que vous le cherchiez? n'est-ce pas···.
Dans cet esprit, il semble simple de définir ces dictionnaires multidimensionnels du côté Python, d'obtenir uniquement les valeurs de coordonnées du plan de UE4 et de calculer du côté Python.
Fondamentalement, si la coordonnée centrale est 0 et le plan de la partie arête (surface de couleur visible) est 151 (la taille du cube est 100, 50 par rayon à partir de la référence centrale, la partie couleur du plan est légèrement plus petite que le cube Puisque 1 est ajouté pour que la couleur soit visible à l'extérieur, une valeur telle que 151) devrait sortir. Nous allons afficher la console et le vérifier dans l'événement BeginPlay de la classe de base du cube.
Ce qui précède est un exemple de plan orange, mais le nom du cube et les coordonnées de référence mondiale (GetWorldLocation) sont affichés (supprimés après confirmation). Ci-dessous, je vais faire une note au format <numéro de ligne> - <numéro de colonne>: <coordonnées de sortie> (je pensais que c'était correct car je l'ai fait en fonction des coordonnées 0, mais je l'ai fait pivoter à l'avance. Il a été confirmé que la valeur cible ne change pas après cela).
Pour faciliter la détermination de la position des couleurs du côté Python, nous ajouterons un processus pour acquérir le tableau de coordonnées de référence mondiale du plan pour chaque couleur dans Blueprint. Je me connecterai avec Python plus tard.
Afin d'unifier au maximum les parties superposées des fonctions, nous normaliserons sauf pour la spécification du plan cible. Je me demandais comment obtenir les acteurs dans une classe BP spécifique, mais il y a un signe que je peux l'obtenir avec un nœud appelé Get All Actors with Tag.
Lorsque j'ai cherché où définir la balise, il semble qu'elle puisse être définie normalement dans la fenêtre Détails.
J'essaierai de faire ce que j'attendais en utilisant ces derniers. ...., mais quand je l'ai essayé, il semble que je ne puisse pas trouver un acteur plat avec ça (je ne comprends pas bien et je m'inquiète environ 10 minutes ...). Le tableau du nombre de valeurs renvoyées sera toujours 0. Apparemment, le nœud Obtenir les composants par balise convient mieux. Je suis un peu confus, mais est-ce parce que la cible d'acquisition est un composant plutôt qu'un acteur? Cela signifie t-il?
La définition d'un acteur est-elle quelque chose comme une classe de plan directeur ou quelque chose qui est placé à un niveau, et un composant qui est stocké dans ces acteurs? Si vous ne le cherchez pas plus tard, il n'aura pas l'air soigné ...
Pour le moment, s'il s'agit d'un nœud Get Components by Tag, il semble que l'avion a été pris normalement, donc je vais reprendre mon esprit et continuer.
Nous répondons en ajoutant un fichier de bibliothèque de fonctions de plan directeur appelé LIB_CubeColor.
Le nom de balise du plan à acquérir est spécifié par l'argument. La balise semble utiliser le type Nom violet. En quoi est-ce différent de String?
Une vérification est placée pour voir si le nom de balise attendu au début de la fonction a un argument afin que vous puissiez immédiatement remarquer si vous avez fait une erreur en spécifiant le nom de la balise.
Si ce n'est pas la valeur du tableau ci-dessus, un message d'erreur sera affiché.
Ensuite, nous faisons une boucle pour chaque cube du monde. Étant donné que le nœud Obtenir les composants par étiquette pour l'acquisition de plan ne peut pas être appelé sans passer par l'acteur de cube, l'acteur de cube est acquis en premier. Après tout, l'acteur est-il plus apte à reconnaître que le parent / composant est l'enfant ...
Obtenez le plan de l'acteur de cube avec le nœud Obtenir les composants par étiquette. Pour la classe des composants, j'ai choisi le composant de maillage statique (car c'était l'affichage du plan sur le BP).
Le nœud Est visible vérifie si le plan est visible. La classe de base du cube a un plan d'une couleur dans l'arrangement lui-même, et comme l'affichage / non-affichage est défini dans le BP de la sous-classe de chaque cube, les masqués sont exclus. Je vais.
Après cela, GetWorldLocation prend les coordonnées mondiales du plan et les ajoute au tableau de valeurs de retour. S'il s'agit d'un flottant, la valeur s'écartera légèrement et, à des fins de comparaison, elle est convertie en un entier avec un nœud Round entre les deux.
Nous créerons le traitement une fois la boucle terminée. Pour le moment, je vais mettre un processus pour vérifier si le nombre de données de résultat est de 9 correctement.
Enfin, il renvoie 3 tableaux et se termine.
Je vais essayer. Pour le moment, sortons les coordonnées du plan orange avant l'animation.
La combinaison correspond à la liste des coordonnées du cube avant que j'ai recherché il y a quelque temps. Ça me va.
Cela s'allonge, alors j'aimerais terminer cet article ici. La prochaine fois, nous procéderons à diverses implémentations autour de l'observation.