[PYTHON] Anfänger möchten mit UE4 so etwas wie einen Rubic Cube erstellen und daraus eine Bibliothek für verbessertes Lernen # 4 machen

Dies ist ein Artikel, in dem Anfänger weiterhin an der Erstellung einer Python-Bibliothek mit UE4 arbeiten werden (fast als Memorandum für mich ...).

Erstens: # 1 Letztes Mal: # 3

Versuchen Sie, mit Python-Skript usw. zu importieren.

Ich habe eines der Python-Module in der PyActor-Blaupause angegeben, aber zu diesem Zeitpunkt habe ich versucht, die Python-Module unter dem anderen Skriptordner zu importieren. Es fühlt sich an, als hätte ich versucht, mit ue.log usw. auf die Konsole auszugeben, und es scheint, dass nur die im Entwurf von PyActor angegebene ausgeführt wird. Es scheint, dass der Import anderer Module selbst keinen Fehler verursacht, aber wenn ich eine in diesem Modul hinzugefügte Funktion aufrufe, tritt ein Fehler auf. Das mit pip installierte kann problemlos verwendet werden, aber im Grunde scheint es besser, es als ein Modul für einen PyActor zu betrachten.

In diesem Fall treten jedoch Probleme bei der allgemeinen Verarbeitung auf. Versuchen Sie also, dem Ordner, in dem die Bibliothek mit pip hinzugefügt wird, einen Ordner hinzuzufügen, wie z. B. common, und importieren Sie ihn.

Der Bibliotheksordner wird von pip von Plugins \ UnrealEnginePython \ Binaries \ Win64 hinzugefügt. Dem Plugin-Verzeichnis wird ein Ordner mit dem Namen common hinzugefügt, und eine Datei mit dem Namen sqlite_utils.py wird hinzugefügt, vorausgesetzt, dass die allgemeine Verarbeitung für SQLite-bezogene Dinge geschrieben wird. Versuchen Sie die folgende Beschreibung in dieser Datei.

sqlite_utils.py


def print_test(ue):
    ue.log('sqlite_utils test')

Fügen Sie als Nächstes die folgende Beschreibung in das von PyActor angegebene Modul ein.

import unreal_engine as ue

from common import sqlite_utils

sqlite_utils.print_test(ue=ue)

Das von pip behandelte Verzeichnis sollte sich im Pfad befinden, daher gehe ich davon aus, dass es importiert werden kann. Lassen Sie uns eine Vorschau in UE4 anzeigen (oder besser gesagt, Play ist das richtige Wort ...).

image.png

Es scheint, dass das importierte Modul erfolgreich aufgerufen wird. Wenn Sie Python schreiben müssen, das mehrere Module umfasst, empfiehlt es sich, das Modul dem Bibliotheksordner hinzuzufügen.

Anstatt unreal_engine direkt in das Modul common.sqlite_utils zu importieren, wird es als Funktionsargument aus dem von PyActor angegebenen Modul übergeben. Wenn dies jedoch nicht das von PyActor angegebene Modul ist, funktioniert es auch als ue.log. nicht. Möglicherweise können Sie den Import des unreal_engine-Moduls nur für das von PyActor angegebene Modul verwenden. Wenn das unreal_engine-Modul auf der Seite des allgemeinen Moduls erforderlich ist, übergeben Sie es dieses Mal als Argument.

Überlegen Sie, wie Sie Python-Skripte testen können

Für die Entwicklung möchte ich einen Test mit einem Python-Skript schreiben. Wie oben erwähnt, gibt es jedoch eine Einschränkung, dass es für jede PyActor-Klasse ein Modul gibt, und diejenigen, die das unreal_engine-Modul betreffen, können nicht getestet werden, ohne mit UE4 zu spielen, sodass ein Testläufer wie pytest nicht verwendet werden kann.

Obwohl es unregelmäßig erscheint, werde ich wie folgt vorgehen.

Vorerst starten wir mit unserem eigenen Testläufer.

py:common.python_test_runner.py


"""Ein Modul, das die Ausführung von Tests für jedes Python-Skript übernimmt.

Notes
-----
- unreal_Zur bequemen Verflechtung des Motormoduls, ohne das Testmodul zu trennen
Arbeiten.
"""

from datetime import datetime
import inspect


def run_tests(ue, target_module):
    """
Führen Sie die für das Zielmodul definierten Tests aus.

    Parameters
    ----------
    ue : unreal_engine
Importiert in das von jedem PyActor angegebene Python-Modul.
UnrealEngine Python-Bibliotheksmodul.
    target_module : module
Das zu testende Modul. Im Modul testen_Vom Präfix
Die Funktion wird ausgeführt.
    """
    ue.log(
        '%s %Testen Sie das s-Modul...'
        % (datetime.now(), target_module.__name__))
    members = inspect.getmembers(target_module)
    for obj_name, obj_val in members:
        if not inspect.isfunction(obj_val):
            continue
        if not obj_name.startswith('test_'):
            continue
        ue.log(
            '%s Zielfunktion: %s' % (datetime.now(), obj_name))
        pre_dt = datetime.now()
        obj_val()
        timedelta = datetime.now() - pre_dt
        ue.log('%s ok. %s Sekunden' % (datetime.now(), timedelta.total_seconds()))

Ich werde die Details später anpassen, aber sobald es eine einfache Implementierung ist. Mit der Funktion getmembers des integrierten Inspect-Moduls können Sie das Element member des im Argument angegebenen Moduls übernehmen. Drehen Sie es also in eine Schleife, prüfen Sie, ob es sich um eine Funktion mit der Funktion isfunction handelt, und verarbeiten Sie die Verarbeitung nur, wenn der Funktionsname das Präfix test_ hat Ich mache es. Es bleibt nur die Beschreibung, die die Testzeit, den Namen des Zielmoduls, den Funktionsnamen usw. an die Konsole ausgibt.

Bereiten Sie in dem von PyActor angegebenen Modul separat ein Modul zum Schreiben von SQLite vor, um Daten im Fluss der UE4 → Python-Bibliothek zu übergeben. Dort habe ich eine Additionsfunktion namens add vorbereitet, um den Betrieb des Testläufers zu überprüfen, die ich später löschen werde.

to_python_sqlite_writer.py


"""Verarbeitet das Schreiben von Daten von UE4 in SQLite für Python-Bibliotheken
Modul für.
"""

import sys
import importlib
import time
import unittest

import unreal_engine as ue

from common import python_test_runner

importlib.reload(python_test_runner)


def add(a, b):
    time.sleep(1)
    return a + b


def test_add():
    added_val = add(a=1, b=3)
    assert added_val == 4


python_test_runner.run_tests(
    ue=ue, target_module=sys.modules[__name__])

Um die Anzeige der Testzeit zu überprüfen, habe ich sie absichtlich für 1 Sekunde in der Funktion in den Ruhezustand versetzt. Wie oben erwähnt, versuche ich, Tests im selben Modul zu schreiben. Schließlich wird der Testläuferprozess auf der obersten Ebene aufgerufen. Sie können Ihr eigenes Modul erhalten, indem Sie sys.moduels [__name __] ausführen.

Darüber hinaus wird das von PyActor angegebene Modul auf der UE4-Seite automatisch neu geladen, nachdem der Code aktualisiert wurde (da das Einstellungs-Popup angezeigt wird), aber es scheint, dass das allgemeine Modul usw. nicht automatisch neu geladen wird. Daher wird die Codeänderung möglicherweise nicht sofort wiedergegeben, daher lade ich sie mit importlib neu, um sie sofort wiederzugeben (möglicherweise wird sie am Ende gelöscht ...).

Lass uns mit UE4 spielen.

image.png

Der Test ist bestanden. Ich werde die Details später bei Bedarf anpassen, aber vorerst scheint es in Ordnung zu sein ...

Legen Sie die Nase in die Testbibliothek

In letzter Zeit verwende ich beim privaten Schreiben von Code häufig Pytest, da Testläufer hervorragende Leistungen erbringen. Diesmal muss ich jedoch nur Assert-Funktionen (assert_equal und assert_raises) verwenden, um die Testbibliothek von nas zu vereinfachen Ich werde es einfügen.

$ ./python.exe -m pip install --target . nose
Successfully installed nose-1.3.7

Um sicherzustellen, dass es auf UE4 funktioniert, werde ich den Code anpassen, den ich vor einiger Zeit in der Testvalidierung geschrieben habe.

to_python_sqlite_writer.py


from nose.tools import assert_equal
...
def test_add():
    added_val = add(a=1, b=3)
    assert_equal(added_val, 4)

image.png

Es scheint gut zu funktionieren.

Fahren wir mit dem Lesen und Schreiben in SQLite fort

Beim letzten Mal habe ich SQLAlchemy für SQLite installiert und bis zu dem Punkt unterstützt, an dem der Import usw. zumindest durchgeführt werden kann. Ich werde jedoch bestätigen, dass es kein Problem mit der Überprüfung gibt, und die vorläufigen Dateien organisieren. Löschen Sie zunächst python_and_h5py_test.py, das zur Überprüfung verwendet wird, z. B. h5py und die damit verbundene Blueprint-Klasse.

Fügen Sie zunächst den Prozess hinzu, um die Zeichenfolge zum Angeben des Pfads von SQLAlchemy of SQLite im allgemeinen Modul abzurufen.

common\sqlite_utils.py


"""Ein Modul, das die allgemeine Verarbeitung in Bezug auf SQLite beschreibt.
"""

import os
import sys

DESKTOP_FOLDER_NAME = 'cubicePuzzle3x3'


def get_sqlite_engine_file_path(file_name):
    """
Rufen Sie den Dateipfad zum Angeben der Engine für SQL Alchemy in SQLite ab.

    Parameters
    ----------
    file_name : str
Der Name der SQL-Zieldatei, die die Erweiterung enthält.

    Returns
    -------
    sqlite_file_path : str
Eine Folge von Pfaden zum Angeben der Engine für SQLite.
        sqlite:///Ab wird ein Ordner für SQLite auf dem Desktop erstellt
In Form bringen.

    Notes
    -----
Es wird erstellt, wenn der Speicherordner nicht vorhanden ist.
    """
    dir_path = os.path.join(
        os.environ['HOMEPATH'], 'Desktop', DESKTOP_FOLDER_NAME)
    os.makedirs(dir_path, exist_ok=True)
    sqlite_file_path = 'sqlite:///{dir_path}/{file_name}'.format(
        dir_path=dir_path,
        file_name=file_name,
    )
    return sqlite_file_path

Als ich es schrieb, bemerkte ich, dass ich keinen Test für ein allgemeines Modul schreiben kann (dieses Modul selbst importiert das ue-Modul nicht).

Fügen Sie daher ein Modul zum Ausführen des Tests des gemeinsamen Moduls und eine Blueprint-Klasse von PyActor hinzu und führen Sie den Test jedes gemeinsamen Moduls darüber aus.

Content\Scripts\run_common_module_tests.py


"""Für Module im allgemeinen Verzeichnis der Python-Plug-Ins
Führen Sie den Test aus.
"""

import sys
import inspect

import unreal_engine as ue

from common import python_test_runner
from common.tests import test_sqlite_utils

NOT_TEST_TARGET_MODULES = [
    sys,
    inspect,
    ue,
    python_test_runner,
]

members = inspect.getmembers(sys.modules[__name__])
for obj_name, obj_val in members:
    if not inspect.ismodule(obj_val):
        continue
    is_in = obj_val in NOT_TEST_TARGET_MODULES
    if is_in:
        continue
    python_test_runner.run_tests(ue=ue, target_module=obj_val)

Die BP-Seite nannte es BP_RunCommonModuleTests.

Ich werde einen Test schreiben. Für das allgemeine Modul ist der Import des ue-Moduls aufgrund des obigen Codes nicht erforderlich, und es gibt kein Problem, selbst wenn die Module getrennt sind. Daher test_ <Modulname mit dem Verzeichnis für das Testmodul wie ein normaler Test Wir werden in Form von> .py fortfahren.

common\tests\test_sqlite_utils.py


"""sqlite_Ein Modul zum Testen des Utils-Moduls.
"""

from nose.tools import assert_equal, assert_true

from common import sqlite_utils


def test_get_sqlite_engine_file_path():
    sqlite_file_path = sqlite_utils.get_sqlite_engine_file_path(
        file_name='test_dbfile.sqlite')
    assert_true(
        sqlite_file_path.startswith('sqlite:///')
    )
    is_in = sqlite_utils.DESKTOP_FOLDER_NAME in sqlite_file_path
    assert_true(is_in)
    assert_true(
        sqlite_file_path.endswith('/test_dbfile.sqlite')
    )

Lass uns UE4 spielen.

image.png

Klingt okay. Jetzt können Sie Tests auch auf der Seite des allgemeinen Moduls schreiben. Das Problem ist, dass es nicht möglich ist, nur bestimmte Module oder bestimmte Funktionen zu testen, die durch Pytest usw. vorbereitet werden. Ich werde über diesen Punkt nachdenken, wenn die Anzahl der Tests zunimmt und es sich ziemlich unangenehm anfühlt (z. B. wenn es möglich ist, nur bestimmte Module schnell zu testen). Es besteht eine hohe Wahrscheinlichkeit, dass die Testzeit auf einem Niveau bleibt, das Sie erst nach Abschluss der Implementierung stört ...

Überprüfen Sie das Verhalten beim Testen von SQLite. Berücksichtigen Sie die Initialisierungsverarbeitung und das Testen, wenn eine Klasse zwischen PyActor-Skripten liegt

Als ich an UE4 arbeitete und mit SQLite testete, gab es einen Fall, in dem die SQLite-Datei gesperrt blieb, auch wenn die Wiedergabe erneut ausgeführt wurde (z. B. wenn in der Mitte ein Fehler auftrat). ).

Es scheint, dass die App einmal gelöscht wird, wenn sie tatsächlich gepackt ist, aber während der Entwicklung ist es schwierig, UE4 jedes Mal neu zu starten, um sie zu entsperren. Stellen Sie sicher, dass Sie die Datums- und Uhrzeitinformationen beim Drücken von Wiedergabe in den Dateinamen einfügen, damit bei jeder Wiedergabe eine andere SQLite-Datei angezeigt wird. Fügen Sie den Namen initializer.py und den PyActor-Entwurf hinzu.

Content\Scripts\initializer.py


"""Ein Modul, das den Prozess beschreibt, der zuerst ausgeführt wird, z. B. wenn das Spiel beginnt.
"""

import json
from datetime import datetime
import os
import sys
import time
import importlib

from nose.tools import assert_equal, assert_true
import unreal_engine as ue

from common import const, file_helper
from common.python_test_runner import run_tests
importlib.reload(const)
importlib.reload(file_helper)


def save_session_data_json():
    """
Informationen zu Beginn einer einzelnen Spielsitzung aufbewahren
Speichern Sie die JSON-Datei.
    """
    session_data_dict = {
        const.SESSION_DATA_KEY_START_DATETIME: str(datetime.now()),
    }
    file_path = file_helper.get_session_json_file_path()
    with open(file_path, mode='w') as f:
        json.dump(session_data_dict, f)
    ue.log('initialized.')


save_session_data_json()


def test_save_session_data_json():
    pre_session_json_file_name = const.SESSION_JSON_FILE_NAME
    const.SESSION_JSON_FILE_NAME = 'test_game_session.json'

    expected_file_path = file_helper.get_session_json_file_path()
    save_session_data_json()
    assert_true(os.path.exists(expected_file_path))
    with open(expected_file_path, 'r') as f:
        json_str = f.read()
    data_dict = json.loads(json_str)
    expected_key_list = [
        const.SESSION_DATA_KEY_START_DATETIME,
    ]
    for key in expected_key_list:
        has_key = key in data_dict
        assert_true(has_key)

    os.remove(expected_file_path)
    const.SESSION_JSON_FILE_NAME = pre_session_json_file_name


run_tests(
    ue=ue,
    target_module=sys.modules[__name__])

Wir werden auch ein gemeinsames Modul für Dateioperationen und deren Tests hinzufügen.

Win64\common\file_helper.py


"""Ein Modul, das die allgemeine Verarbeitung von Dateivorgängen beschreibt.
"""

import os
import time
import json
from datetime import datetime

from common.const import DESKTOP_FOLDER_NAME
from common import const


def get_desktop_data_dir_path():
    """
Rufen Sie ein Verzeichnis zum Speichern von Daten auf dem Desktop ab.

    Returns
    -------
    dir_path : str
Verzeichnispfad zum Speichern der abgerufenen Desktop-Daten.

    Notes
    -----
Es wird erstellt, wenn der Speicherordner nicht vorhanden ist.
    """
    dir_path = os.path.join(
        os.environ['HOMEPATH'], 'Desktop', DESKTOP_FOLDER_NAME)
    os.makedirs(dir_path, exist_ok=True)
    return dir_path


def get_session_json_file_path():
    """
Informationen zu Beginn einer einzelnen Spielsitzung aufbewahren
Rufen Sie den Pfad der JSON-Datei ab.

    Returns
    -------
    file_path : str
Zieldateipfad.
    """
    file_path = os.path.join(
        get_desktop_data_dir_path(),
        const.SESSION_JSON_FILE_NAME
    )
    return file_path


def get_session_start_time_str(remove_symbols=True):
    """
Rufen Sie die Datums- und Zeitzeichenfolge zu Beginn einer Spielsitzung aus der JSON-Datei ab.
Es wird für SQLite-Dateinamen verwendet.

    Parameters
    ----------
    remove_symbols : bool, default True
Entfernen Sie das Symbol aus der Rückgabewertzeichenfolge, um einen Wert mit nur Ganzzahlen halber Breite zu erhalten
Ob konvertiert werden soll.

    Returns
    -------
    session_start_time_str : str
Eine Reihe von Daten und Zeiten zu Beginn einer Spielsitzung.
    """
    time.sleep(0.1)
    file_path = get_session_json_file_path()
    with open(file_path, mode='r') as f:
        data_dict = json.load(f)
    session_start_time_str = str(
        data_dict[const.SESSION_DATA_KEY_START_DATETIME])
    if remove_symbols:
        session_start_time_str = session_start_time_str.replace('-', '')
        session_start_time_str = session_start_time_str.replace('.', '')
        session_start_time_str = session_start_time_str.replace(':', '')
        session_start_time_str = session_start_time_str.replace(' ', '')
    return session_start_time_str

Fügen Sie außerdem ein Modul für die allgemeine SQLite-Verarbeitung hinzu.

Win64\common\sqlite_utils.py


"""Ein Modul, das die allgemeine Verarbeitung in Bezug auf SQLite beschreibt.
"""

import os
import sys

import sqlalchemy
from sqlalchemy.orm import sessionmaker

from common import file_helper


def get_sqlite_engine_file_path(file_name):
    """
Rufen Sie den Dateipfad zum Angeben der Engine für SQL Alchemy in SQLite ab.

    Parameters
    ----------
    file_name : str
Der Name der SQL-Zieldatei, die die Erweiterung enthält.

    Returns
    -------
    sqlite_file_path : str
Eine Folge von Pfaden zum Angeben der Engine für SQLite.
        sqlite:///Ab wird ein Ordner für SQLite auf dem Desktop erstellt
In Form bringen.

    Notes
    -----
Es wird erstellt, wenn der Speicherordner nicht vorhanden ist.
    """
    dir_path = file_helper.get_desktop_data_dir_path()
    sqlite_file_path = 'sqlite:///{dir_path}/{file_name}'.format(
        dir_path=dir_path,
        file_name=file_name,
    )
    return sqlite_file_path


def create_session(sqlite_file_name, declarative_meta):
    """
Erstellen Sie eine SQLite-Sitzung.

    Parameters
    ----------
    sqlite_file_name : str
Der Name der SQLite-Zieldatei.
    declarative_meta : DeclarativeMeta
Ein Objekt, das die Metadaten jeder Tabelle in der Ziel-SQLite speichert.

    Returns
    -------
    session : Session
Die generierte SQLite-Sitzung.
    """
    sqlite_file_path = get_sqlite_engine_file_path(
        file_name=sqlite_file_name)
    engine = sqlalchemy.create_engine(sqlite_file_path, echo=True)
    declarative_meta.metadata.create_all(bind=engine)
    Session = sessionmaker(bind=engine)
    session = Session()
    return session

Win64\common\tests\test_sqlite_utils.py


"""sqlite_Ein Modul zum Testen des Utils-Moduls.
"""

import os
from nose.tools import assert_equal, assert_true
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer

from common import sqlite_utils, file_helper


def test_get_sqlite_engine_file_path():
    sqlite_file_path = sqlite_utils.get_sqlite_engine_file_path(
        file_name='test_dbfile.sqlite')
    assert_true(
        sqlite_file_path.startswith('sqlite:///')
    )
    is_in = file_helper.DESKTOP_FOLDER_NAME in sqlite_file_path
    assert_true(is_in)
    assert_true(
        sqlite_file_path.endswith('/test_dbfile.sqlite')
    )


def test_create_session():
    if not os.path.exists(file_helper.get_session_json_file_path()):
        return
    session_start_time_str = file_helper.get_session_start_time_str()
    sqlite_file_name = 'test_%s.sqlite' % session_start_time_str
    expected_file_path = sqlite_utils.get_sqlite_engine_file_path(
        file_name=sqlite_file_name)
    if os.path.exists(expected_file_path):
        os.remove(expected_file_path)

    declarative_meta = declarative_base()

    class TestTable(declarative_meta):
        id = Column(Integer, primary_key=True)
        __tablename__ = 'test_table'

    session = sqlite_utils.create_session(
        sqlite_file_name=sqlite_file_name,
        declarative_meta=declarative_meta)

    test_data = TestTable()
    session.add(instance=test_data)
    session.commit()
    query_result = session.query(TestTable)
    for test_data in query_result:
        assert_true(isinstance(test_data.id, int))

    expected_file_path = expected_file_path.replace('sqlite:///', '')
    assert_true(
        os.path.exists(expected_file_path))

    session.close()
    os.remove(expected_file_path)

Obwohl es in Ordnung ist, initializer.py einzufügen, muss bei der tatsächlichen Verwendung der Prozess des mit SQLite verbundenen Moduls nach dem Initialisierer ausgeführt werden. Andererseits ist unklar, welches Modul zuerst in PyActor ausgeführt wird (in einigen Fällen werden die Skripte der obersten Ebene anderer Module vor dem Initialisierer ausgeführt).

Wenn dies nicht nach dem Initialisierer erfolgt, geben Sie die Klasse mit PyActor an und starten Sie sie über die Methode begin_play (über begin_play wird sie nach der Verarbeitung der obersten Ebene jedes Moduls ausgeführt. Scheint zumindest so weit ich es versucht habe).

Content\Scripts\to_python_sqlite_writer.py


"""Verarbeitet das Schreiben von Daten von UE4 in SQLite für Python-Bibliotheken
Modul für.
"""

import sys
import importlib
import time
import unittest
import time

import unreal_engine as ue
from nose.tools import assert_equal, assert_true
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

from common import python_test_runner, sqlite_utils, file_helper

importlib.reload(python_test_runner)
importlib.reload(sqlite_utils)
declarative_meta = declarative_base()


class ToPythonSqliteWriter:

    class TestTable(declarative_meta):
        id = Column(Integer, primary_key=True)
        name = Column(String(length=256))
        __tablename__ = 'test_table'

    def begin_play(self):
        """
Eine Funktion, die ausgeführt wird, wenn das Spiel beginnt.

        Notes
        -----
Es wird nach der Verarbeitung jedes Moduls auf oberster Ebene ausgeführt.
        """
        self.session_start_time_str = \
            file_helper.get_session_start_time_str()
        self.SQLITE_FILE_NAME = 'to_python_from_ue4_%s.sqlite'\
            % self.session_start_time_str

        self.session = sqlite_utils.create_session(
            sqlite_file_name=self.SQLITE_FILE_NAME,
            declarative_meta=declarative_meta,
        )
        python_test_runner.run_begin_play_test(
            begin_play_test_func=self.test_begin_play
        )

    def test_begin_play(self):
        assert_equal(
            self.session_start_time_str,
            file_helper.get_session_start_time_str(),
        )
        is_in = 'to_python_from_ue4_' in self.SQLITE_FILE_NAME
        assert_true(is_in)
        is_in = '.sqlite' in self.SQLITE_FILE_NAME
        assert_true(is_in)
        query_result = self.session.query(self.TestTable)


python_test_runner.run_tests(
    ue=ue, target_module=sys.modules[__name__])

Da der aktuell hinzugefügte Testläufer nur Top-Level-Testläufer unterstützt, fügen wir eine Funktion namens "run_begin_play_test" ein, um einen Test für eine Methode auszuführen, die sich auf ein spezielles UE4-Ereignis von begin_play bezieht.

Win64\common\python_test_runner.py


def run_begin_play_test(begin_play_test_func):
    """
Beginn der von PyActor angegebenen Klasse_Test auf Spielmethode
Lauf.

    Parameters
    ----------
    begin_play_test_func : function
Ziel beginnen_Testen Sie die Spielmethode.
    """
    begin_play_test_func()

Derzeit gibt es fast keinen Inhalt, aber wir werden einen Prozess einfügen, damit er erst während der späteren Entwicklung ausgeführt wird (da wir in diesem Umfang noch nicht mit der UE4-Seite zusammenarbeiten konnten).

Überprüfen Sie die generierte SQLite-Datei

Wenn Sie überprüfen, ob der Test erfolgreich ist, und den Desktop überprüfen, können Sie feststellen, dass die Datei vorerst generiert wurde.

image.png

Lassen Sie uns den Inhalt ein wenig überprüfen. Installieren Sie DB Browser for SQLite und überprüfen Sie den Inhalt.

image.png

Derzeit scheint SQLite über UE4 einwandfrei zu funktionieren.

Lassen Sie Python den Wert annehmen, ob es sich um eine gepackte Umgebung handelt

Ermöglichen Sie auch auf der Python-Seite, den Wert des Knotens Is Packaged for Distribution zu übernehmen, sodass Tests usw. nur während der Entwicklung ausgeführt werden. Da der Funktionsaufruf jedoch nur über uobject ausgeführt werden kann, muss er die Klasse durchlaufen und kann nicht auf der obersten Ebene des Initialisierers verarbeitet werden. Nun, es geht nur darum, unnötige Verarbeitung zu reduzieren, und es ist nicht möglich, also lassen Sie uns fortfahren, ohne uns um die Details zu kümmern ...

Da wir die Funktion auf der UE4-Blaupause von Python aus aufrufen müssen, fügen Sie zuerst die Zielfunktion zur Blaupause hinzu.

image.png

Es ist eine Funktion, die einfach den Wert der vorbereiteten Funktion zurückgibt. Ist es möglich, eine direkt vorbereitete Funktion (grün statt lila) von Python ohne diese Funktion aufzurufen? Ich habe es versucht, aber es wurde ohne so etwas gespielt, also werde ich es normal vorbereiten.

Geben Sie außerdem die Klasse in der Blaupause an.

image.png

Wir werden mit der Unterstützung auf der Python-Seite fortfahren. Wir müssen das Objekt der Klasse durchgehen, also bereiten Sie die Klasse vor.

Content\Scripts\initializer.py


class Initializer:

    def begin_play(self):
        """
Eine Funktion, die ausgeführt wird, wenn das Spiel beginnt.
        """
        self.is_packaged_for_distribution = \
            self.uobject.isPackagedForDistribution()[0]
        _update_packaged_for_distribution_value(
            is_packaged_for_distribution=self.is_packaged_for_distribution)


def _update_packaged_for_distribution_value(is_packaged_for_distribution):
    """
Gibt an, ob die Umgebung für den Vertrieb (für die Produktion) verpackt ist.
Aktualisieren Sie den Booleschen Wert.

    Parameters
    ----------
    is_packaged_for_distribution : bool
Zu setzender Boolescher Wert.
    """
    file_path = file_helper.get_session_json_file_path()
    if os.path.exists(file_path):
        with open(file_path, mode='r') as f:
          session_data_dict = json.load(f)
    else:
        session_data_dict = {}
    if is_packaged_for_distribution:
        is_packaged_for_distribution_int = 1
    else:
        is_packaged_for_distribution_int = 0
    with open(file_path, mode='w') as f:
        session_data_dict[
            const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION] = \
            is_packaged_for_distribution_int
        json.dump(session_data_dict, f)


def test__update_packaged_for_distribution_value():
    pre_session_json_file_name = const.SESSION_JSON_FILE_NAME
    const.SESSION_JSON_FILE_NAME = 'test_game_session.json'
    expected_file_path = file_helper.get_session_json_file_path()

    _update_packaged_for_distribution_value(
        is_packaged_for_distribution=True)
    with open(expected_file_path, mode='r') as f:
        session_data_dict = json.load(f)
    assert_equal(
        session_data_dict[
            const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION],
        1
    )

    _update_packaged_for_distribution_value(
        is_packaged_for_distribution=False)
    with open(expected_file_path, mode='r') as f:
        session_data_dict = json.load(f)
    assert_equal(
        session_data_dict[
            const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION],
        0
    )

    os.remove(expected_file_path)
    const.SESSION_JSON_FILE_NAME = pre_session_json_file_name

Obwohl der Wert als "self.uobject.isPackagedForDistribution () [0]" verwendet wird, wird er auf der Python-Seite als Taple übergeben, auch wenn es nur einen Rückgabewert der Blueprint-Funktion gibt. Ich werde. Selbst wenn ich auf UE4 auf die Konsole ausgeben möchte, ist es False, aber ich muss mir Sorgen machen, dass der Zweig einige Minuten lang nicht funktioniert. Anscheinend scheint die Konsolenausgabe direkt wie False oder True zu sein, anstatt wie die Tapple-Anzeige wie (False,). Das ist verwirrend ... (zumindest ein Komma ...) Als ich den Typ anzeigte, bemerkte ich, dass es sich um ein Tupel handelte.

Ich werde auch den Wertschöpfungsprozess hinzufügen.

Win64\common\file_helper.py


def get_packaged_for_distribution_bool():
    """
Ruft den booleschen Wert ab, ob es sich zur Verteilung in einem gepackten Zustand befindet oder nicht.

    Notes
    -----
Nur zu Beginn des ersten Starts der Zeitpunkt, zu dem der Wert vor dem Speichern des Werts nicht normal abgerufen werden kann
Existiert. In diesem Fall wird False zurückgegeben.

    Returns
    -------
    is_packaged_for_distribution : bool
Richtig und für den Vertrieb (für die Produktion) verpackt, Falsch
Zur Entwicklung.
    """
    file_path = get_session_json_file_path()
    if not os.path.exists(file_path):
        return False
    with open(file_path, mode='r') as f:
        json_str = f.read()
        if json_str == '':
            session_data_dict = {}
        else:
            session_data_dict = json.loads(json_str)
    has_key = const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION \
        in session_data_dict
    if not has_key:
        return False
    is_packaged_for_distribution = session_data_dict[
        const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION]
    if is_packaged_for_distribution == 1:
        return True
    return False

Win64\common\tests\test_file_helper.py


def test_get_packaged_for_distribution_bool():
    pre_session_json_file_name = file_helper.const.SESSION_JSON_FILE_NAME
    file_helper.const.SESSION_JSON_FILE_NAME = 'test_game_session.json'
    file_path = file_helper.get_session_json_file_path()
    if os.path.exists(file_path):
        os.remove(file_path)

    #Überprüfen Sie den Rückgabewert, wenn die Datei nicht vorhanden ist.
    is_packaged_for_distribution = \
        file_helper.get_packaged_for_distribution_bool()
    assert_false(is_packaged_for_distribution)

    #Wenn die Datei vorhanden ist, aber leere Zeichen gesetzt sind
    #Überprüfen Sie den zurückgegebenen Wert.
    with open(file_path, 'w') as f:
        f.write('')
    is_packaged_for_distribution = \
        file_helper.get_packaged_for_distribution_bool()
    assert_false(is_packaged_for_distribution)

    #Überprüfen Sie den Rückgabewert, wenn der Wert auf 1 gesetzt ist.
    with open(file_path, 'w') as f:
        session_data_dict = {
            const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION: 1,
        }
        json.dump(session_data_dict, f)
    is_packaged_for_distribution = \
        file_helper.get_packaged_for_distribution_bool()
    assert_true(
        is_packaged_for_distribution
    )

    #Überprüfen Sie den Rückgabewert, wenn der Wert auf 0 gesetzt ist.
    with open(file_path, 'w') as f:
        session_data_dict = {
            const.SESSION_DATA_KEY_IS_PACKAGED_FOR_DISTRIBUTION: 0,
        }
        json.dump(session_data_dict, f)
    is_packaged_for_distribution = \
        file_helper.get_packaged_for_distribution_bool()
    assert_false(is_packaged_for_distribution)

    os.remove(file_path)
    file_helper.const.SESSION_JSON_FILE_NAME = pre_session_json_file_name

Platzieren Sie die Zweige wie unten gezeigt in verschiedenen Teilen des Testläufers.

Win64\common\python_test_runner.py


def run_tests(ue, target_module):
    ...
    is_packaged_for_distribution = \
        file_helper.get_packaged_for_distribution_bool()
    if is_packaged_for_distribution:
        return
    ...

Reflektieren Sie den zufälligen Zustand der Anfangsanzeige

Derzeit beginnt es mit den ausgerichteten Farben. Drehen Sie es also nach dem Zufallsprinzip, damit es sich wie ein normaler Rubikwürfel anfühlt.

Wir haben einen sofortigen Rotationsprozess, der nicht animiert wird. Daher werden wir diesen verwenden, um dem Entwurf Funktionen hinzuzufügen. Da der Funktionsname in der Gym-Bibliothek von OpenAI für den Umsetzvorgang der Umgebung zurückgesetzt wird, legen Sie ihn entsprechend zurück (er wird nicht nur zu Beginn, sondern auch beim erneuten Verschieben verwendet).

Ermitteln Sie zunächst den Wert, wie oft gedreht werden soll. Es scheint, dass so etwas wie NumPys Randint im Random Integer-Knoten vorbereitet ist. Verwenden Sie das also. Es scheint, dass der Minimalwert auf 0 und der Maximalwert auf Max -1 gesetzt ist. Wenn es unverändert bleibt und 0 herauskommt, wird die Oberfläche von Anfang an ausgerichtet ..., sodass sie mindestens 200 Mal über den MAX-Knoten gedreht werden sollte.

image.png

Bereiten Sie einen booleschen Wert als lokale Variable vor, um zu bestimmen, in welche Richtung in der Schleife gedreht werden soll. Setzen Sie es außerdem am Anfang der Schleife auf False.

image.png

Zunächst XYZ Der Wahrheitswert, in welche Richtung gedreht werden soll, wird zufällig als wahr bestimmt. Versuchen Sie zum ersten Mal, den Switch-Knoten zu verwenden. Wenn Sie an das Programmieren gewöhnt sind, können Sie es problemlos verwenden.

image.png

Dann die Drehrichtung. Entscheiden Sie nach dem Zufallsprinzip, ob Sie nach links oder rechts oder nach oben oder unten drehen möchten.

image.png

Berechnen Sie abschließend den Wert, z. B. welche Spalte gedreht werden soll. Da ein Wert von 1 bis 3 berechnet werden muss, wird der Wert von 0 bis 2 vom Random Integer-Knoten ausgegeben und dann um 1 erhöht.

image.png

Danach verzweigen Sie mit Branch gemäß dem ermittelten Wert und führen die erforderliche Drehung aus, und es ist abgeschlossen.

image.png

Ich werde es bewegen.

image.png

Die Farbe kam gut weg. Ich bin nicht sicher, wie ich es drehen soll, damit alle Farben ausgerichtet sind.

Versuchen Sie es erneut.

image.png

Es fühlt sich anders an als das vorherige. Klingt okay. Es ist ein Prozess mit vielen Schleifen, aber in meiner Desktop-Umgebung endet er sofort und es ist nicht erforderlich, insbesondere FPS zu warten. Erstens ist die Person, die mit verbessertem Lernen durch Lernen arbeitet, der Desktop und ein PC mit einigermaßen guten Spezifikationen. Die meisten von ihnen werden es sein, also schätze ich, dass es kein Problem gibt.

Bereiten Sie einen Prozess vor, um den booleschen Wert zu ermitteln, ob er rotiert oder nicht.

Ich habe Probleme, wenn während der Animation eine weitere Drehung ausgeführt wird. Irgendwann werde ich versuchen, Fehler auf der Python-Seite zu kontrollieren, aber vorerst werde ich dem Blueprint eine Implementierung hinzufügen und in Zusammenarbeit damit den Python-Code schreiben.

Der Name wird mit einer Blaupause erstellt, die PyActor mit dem Namen BP_Action gemäß den Bestimmungen der Gym-Bibliothek erbt.

Es ist in Ordnung, eine Blaupause hinzuzufügen, aber wie bringt man die Schauspieler auf das Level ...? Als ich dachte, wurde es in das offizielle Dokument geschrieben.

Suche nach Schauspielern nach Blaupause

Es scheint, dass Sie mit dem Knoten Alle Schauspieler der Klasse abrufen arbeiten können, also werde ich es versuchen.

image.png

Es ist ein Knoten, der sich einfach in einer Schleife dreht und den Namen auf dem Bildschirm druckt.

image.png

Es wird richtig angezeigt. Einfach.

Damit können wir anscheinend eine Funktion vorbereiten, um den Wert zu ermitteln, ob der Akteur des rotierenden Würfels in der Blaupause von BP_Action vorhanden ist oder nicht.

Fügen Sie der Blaupause der Basisklasse von BP_CubeBase eine Funktion namens isRotating hinzu.

Da wir zuvor eine Funktion vorbereitet hatten, um den Wahrheitswert der Zielrotation in jeder Drehrichtung zu erhalten, werden wir ein Array von Wahrheitswerten mit lokalen Variablen vorbereiten und das Array beim Aufrufen integrieren. Es scheint, dass Sie einem Array mit dem APPEND-Knoten ein Array hinzufügen können (erweiterungsähnliches Verhalten in Python).

image.png

Als ich mit dem Hinzufügen einer Reihe von Booleschen Werten zum Array fertig war, bereitete ich eine Funktion vor, die True zurückgibt, wenn einer der Booleschen Werte im Array True ist, indem ich ihn an einer anderen Stelle (dieser) übergebe. Ich habe 3 Sequenzen angegeben, aber ...), also werde ich das verwenden.

image.png

Erstellen Sie einen Entwurf für BP_Action mit der Funktion isAnyCubeRotating.

image.png

Verwenden Sie Get All Actors Of Class, das ich vor einiger Zeit erwähnt habe, um ein Array abzurufen und es zu durchlaufen.

image.png

Wenn sich ein Würfel in der Schleife dreht, wird er an den True Return-Knoten gesendet. Wenn die Schleife beendet ist, ohne einen rotierenden zu finden, wird er an den False Return Node gesendet.

Geben Sie im BP_Action-Entwurf auch das Python-Modul und die Klasse an.

image.png

Fügen Sie auf der Python-Seite ein Modul namens action.py hinzu und schreiben Sie den Prozess. Ich habe den Rotationsprozess usw. noch nicht von der Python-Seite aus geschrieben, daher werde ich den Test usw. einmal überspringen (ich werde ihn schreiben, wenn der Funktionstest vorhanden ist, aber ich verstehe gut, dass er nicht vorhanden ist und UE4-bezogene Dinge Weil nicht). Versuchen Sie auch die Konsolenausgabe mit einem Häkchen (im Moment sollte immer False zurückgegeben werden).

Content\Scripts\action.py


"""Ein Modul, das einige Verarbeitungsschritte im Zusammenhang mit der Verhaltenssteuerung im Agenten beschreibt.
"""

import unreal_engine as ue


class Action:

    def tick(self, delta_time):
        """
Eine Funktion, die ungefähr in jedem Frame während des Spiels ausgeführt wird.

        Parameters
        ----------
        delta_time : float
Verstrichene Sekunden seit dem letzten Tick-Aufruf.
        """
        ue.log(is_any_cube_rotating(action_instance=self))


def is_any_cube_rotating(action_instance):
    """
Ruft den booleschen Wert ab, ob sich ein Würfel dreht.

    Parameters
    ----------
    action_instance : Action
Eine Instanz der Action-Klasse mit uobject.

    Returns
    ----------
    is_rotating : bool
True wird gesetzt, wenn sich ein Würfel dreht.
    """
    is_rotating = action_instance.uobject.isAnyCubeRotating()[0]
    return is_rotating

Wenn Sie sich das Protokoll ansehen, können Sie sehen, dass False ausgegeben wird.

...
LogPython: False
LogPython: False
LogPython: False
LogPython: False
...

Es scheint vorerst in Ordnung zu sein. Schalten Sie den Konsolenausgang aus und fahren Sie mit dem nächsten fort.

Setzen Sie NumPy ein

Als ich die hdf5-Beziehung deinstallierte, deinstallierte ich auch NumPy, das als Abhängigkeiten installiert wurde, aber schließlich wollte ich es verwenden, sodass ich nur NumPy einschließen werde.

$ ./python.exe -m pip install --target . numpy
Successfully installed numpy-1.17.3

Förderung der Umsetzung handlungsbezogener Kontrollen.

Zunächst habe ich die Zuordnung von Aktionsnummern definiert. Stellen Sie sicher, dass Sie nach Duplikaten suchen und prüfen, ob diese ordnungsgemäß in der Liste enthalten sind.

Content\Scripts\action.py


import numpy as np
...
ACTION_ROTATE_X_LEFT_1 = 1
ACTION_ROTATE_X_LEFT_2 = 2
ACTION_ROTATE_X_LEFT_3 = 3
ACTION_ROTATE_X_RIGHT_1 = 4
ACTION_ROTATE_X_RIGHT_2 = 5
ACTION_ROTATE_X_RIGHT_3 = 6
ACTION_ROTATE_Y_UP_1 = 7
ACTION_ROTATE_Y_UP_2 = 8
ACTION_ROTATE_Y_UP_3 = 9
ACTION_ROTATE_Y_DOWN_1 = 10
ACTION_ROTATE_Y_DOWN_2 = 11
ACTION_ROTATE_Y_DOWN_3 = 12
ACTION_ROTATE_Z_UP_1 = 13
ACTION_ROTATE_Z_UP_2 = 14
ACTION_ROTATE_Z_UP_3 = 15
ACTION_ROTATE_Z_DOWN_1 = 16
ACTION_ROTATE_Z_DOWN_2 = 17
ACTION_ROTATE_Z_DOWN_3 = 18

ACTION_LIST = [
    ACTION_ROTATE_X_LEFT_1,
    ACTION_ROTATE_X_LEFT_2,
    ACTION_ROTATE_X_LEFT_3,
    ACTION_ROTATE_X_RIGHT_1,
    ACTION_ROTATE_X_RIGHT_2,
    ACTION_ROTATE_X_RIGHT_3,
    ACTION_ROTATE_Y_UP_1,
    ACTION_ROTATE_Y_UP_2,
    ACTION_ROTATE_Y_UP_3,
    ACTION_ROTATE_Y_DOWN_1,
    ACTION_ROTATE_Y_DOWN_2,
    ACTION_ROTATE_Y_DOWN_3,
    ACTION_ROTATE_Z_UP_1,
    ACTION_ROTATE_Z_UP_2,
    ACTION_ROTATE_Z_UP_3,
    ACTION_ROTATE_Z_DOWN_1,
    ACTION_ROTATE_Z_DOWN_2,
    ACTION_ROTATE_Z_DOWN_3,
]
...
def test_ACTION_LIST():
    assert_equal(
        len(ACTION_LIST), len(np.unique(ACTION_LIST))
    )
    members = inspect.getmembers(sys.modules[__name__])
    for obj_name, obj_val in members:
        if not obj_name.startswith('ACTION_ROTATE_'):
            continue
        assert_true(isinstance(obj_val, int))
        is_in = obj_val in ACTION_LIST
        assert_true(is_in)


python_test_runner.run_tests(
    ue=ue,
    target_module=sys.modules[__name__])

Fügen Sie BP_Action die Funktion jeder Aktion gemäß dem definierten Konstantennamen hinzu. Ich dachte, aber die Funktion zum Berechnen der Liste der Zielwürfel durch Rotation wurde in die Ebenen-Blaupause geschrieben. Ich habe den Eindruck, dass das Aufrufen dieser Funktion von BP_Action aus ein Ärger zu sein scheint ... (Ich bin verwirrt, weil ich mich erst darauf beziehen kann, wenn ich mich daran gewöhnt habe ...) Referenz: Überlegen Sie, wie Sie auf die Ebenen-Blaupause verweisen können

Ich kann nicht anders, als zu trauern. Deshalb füge ich der Blaupause der Basisklasse des Würfels zuerst eine Funktion hinzu, um den booleschen Wert zu ermitteln, ob es sich bei jeder Umdrehung um den Zielwürfel handelt (dann um den Würfel in der Ebene). Kann mit dem Knoten Alle Akteure der Klasse abrufen ...) aufgenommen werden.

Probieren Sie die Funktionsbibliothek aus

Bevor ich fortfuhr, schrieb ich so etwas wie einen Assert-Helfer in die Level-Blaupause, aber es kann unpraktisch sein, weil es nicht in der BP-Klasse verwendet werden kann, daher werde ich überlegen, es anzupassen.

Als ich es nachgeschlagen habe, scheint es eine Funktionsbibliothek zu geben.

Was ist eine Funktionsbibliothek? Eine Blaupause, mit der Sie über verschiedene Funktionen verfügen können, auf die von überall an einem Ort zugegriffen werden kann. Im Gegensatz zu normalen Blaupausen kann es keine Variablen enthalten und es gibt kein Ereignisdiagramm. Sie können keine Makros erstellen, sondern nur Funktionen. Die darin geschriebenen Funktionen können unabhängig von der Blaupause, dem Akteur oder der Ebene verwendet werden. [UE4] Gute Verwendung der Funktionsbibliothek und der Makrobibliothek

Wie man es macht, wurde in folgendem Artikel geschrieben: bow:

UE4-Funktionsbibliothek erstellen (Blueprint-Funktionsbibliothek)

Wie sollte der Funktionsbibliotheksordner laut Konvention heißen ...? In Videos und Büchern wurden Bräuche wie BluePrints für Blaupausen, BP_ am Anfang von Dateinamen, Materialien für Materialien usw. eingeführt, aber wie war die Funktionsbibliothek überhaupt? Wurde die Funktionsbibliothek eingeführt ... (Es ist nicht gut, sie zu vergessen, wenn Sie sie nicht ausgeben ...)

Derzeit ist es kein Job. Sie können ihn also auch als Ordner "Ordner" bezeichnen. Versuchen Sie, dem Dateinamen LIB_ voranzustellen.

image.png

Es scheint, dass Sie es erstellen können, indem Sie eine neue Datei im Inhaltsbrowser hinzufügen und Blueprints → Blueprint Function Library auswählen.

Ich habe es LIB_Testing genannt, weil ich Testbeziehungen hinzufügen möchte.

image.png

Wenn ich es öffne, sieht es so aus. Es sieht aus wie eine Blaupause mit einer sehr einfachen Struktur, die nur aus Funktionen und lokalen Variablen besteht. Verschieben Sie die Funktion, die Sie verschieben möchten, hierher und ersetzen Sie die Funktion auf der vorhandenen Ebene BP durch diese.

Es scheint, dass die der Bibliothek hinzugefügten Funktionen von der Ebene BP oder anderen BP-Klassen aufgerufen werden können, ohne etwas Besonderes zu tun.

image.png

Dies erleichtert das Überprüfen der Lichtwerte in der BP-Klasse.

Fügen Sie die Verarbeitung hinzu, um den booleschen Wert zu ermitteln, ob es sich um das Rotationsziel der Basisklasse des Cubes handelt

Wie oben erwähnt, können wir den booleschen Wert, ob es sich um das Rotationsziel handelt, von der Basisklasse von BP_CubeBase abrufen und von BP_Action aus aufrufen.

Da wir eine Funktion erstellen, die einen booleschen Wert zurückgibt, wie unten gezeigt, im Bereich von 1 bis 3 in XYZ, werden wir 9 Funktionen hinzufügen.

image.png

Der gemeinsame Teil ist in verschiedene Funktionen unterteilt. Außerdem ist das Array des Positionstyps des Zielwürfels in der Drehung des Ziels eine Konstante und wurde bereits zuvor vorbereitet, sodass wir dies verwenden werden.

image.png

Drehen Sie innerhalb des allgemeinen Prozesses die Schleife für das Array

image.png

Wenn der Typwert der Position des aktuellen Cubes mit dem Wert des Indexarrays in der aktuellen Schleife übereinstimmt, wird True zurückgegeben, und wenn auch nach dem Ende der Schleife kein entsprechender Wert vorhanden ist, wird False zurückgegeben.

Ich werde die Details weglassen, aber ich werde einen Test schreiben, um das Verhalten des Prozesses bis zu einem gewissen Grad zu bestätigen.

image.png

Jetzt, da wir 9 Umdrehungen fertig haben und der Test nicht erwischt wird, können wir weitermachen. Versuchen wir, BP_Action eine Rotationsfunktion hinzuzufügen. Zunächst gehen wir von der einfachen, nicht animierten Rotationsart aus.

Holen Sie sich zuerst den Schauspieler des Würfels und drehen Sie die Schleife.

image.png

Legen Sie danach einen Zweig fest, der auf dem Wahrheitswert basiert, ob es sich um das zuvor vorbereitete Rotationsziel handelt, und drehen Sie ihn, wenn es wahr ist.

image.png

Ich werde es auch auf der Python-Seite versuchen.

Content\Scripts\action.py


class Action:

    total_delta_time = 0
    is_rotated = False

    def tick(self, delta_time):
        """
Eine Funktion, die ungefähr in jedem Frame während des Spiels ausgeführt wird.

        Parameters
        ----------
        delta_time : float
Verstrichene Sekunden seit dem letzten Tick-Aufruf.
        """
        self.total_delta_time += delta_time
        if self.total_delta_time > 5 and not self.is_rotated:
            self.uobject.rotateXLeftImmediately1()
            self.is_rotated = True

Vorerst dreht es sich sofort alle 5 Sekunden.

20191110_2.gif

Ich habe versucht, eine Vorschau anzuzeigen, aber es sieht in Ordnung aus. Wir werden andere Rotationsverarbeitungen auf die gleiche Weise erstellen, aber vorher die Funktion zum Testen des Rotationsergebnisses auf die Seite der Funktionsbibliothek verschieben, damit sie von der Seite BP_Action referenziert werden kann, und die Funktion auf der Ebenenseite ersetzen Und schneide es ab.

Fügen Sie nach dem Verschieben eine Funktion zum Überprüfen des Werts nach Drehung des Ziels bis zum Ende der Funktion hinzu, die diesmal zu BP_Action hinzugefügt wurde, und zeigen Sie eine Vorschau an, um sicherzustellen, dass es nicht von der Überprüfung erfasst wird.

image.png

Es scheint vorerst in Ordnung zu sein. Fügen Sie also eine Reihe von Rotationen in andere Richtungen hinzu und rufen Sie es von der Python-Seite aus auf, um den Vorgang zu überprüfen.

20191112_1.gif

Der Prozess der sofortigen Rotation über Python scheint in Ordnung zu sein. Das nächste Mal werde ich daran arbeiten, den animierten Rotationsprozess mit Python zu verbinden (der Artikel wird länger, daher lasse ich diesen Artikel hier).

Punkte der Besorgnis

Zusammenfassung der Referenzseite

Recommended Posts

Anfänger möchten mit UE4 so etwas wie einen Rubic Cube erstellen und daraus eine Bibliothek für verbessertes Lernen # 4 machen
Anfänger möchten mit UE4 so etwas wie einen Rubic Cube erstellen und daraus eine Bibliothek für erweitertes Lernen # 5 machen
Anfänger möchten mit UE4 so etwas wie einen Rubic Cube erstellen und daraus eine Bibliothek für verbessertes Lernen # 6 machen
Ich möchte mit verstärkendem Lernen einen Berg besteigen
[Einführung] Ich möchte mit Python einen Mastodon-Bot erstellen! 【Anfänger】
Erweitertes Lernen 35 Python Lokale Entwicklung, fügen Sie einen Link zu myModule hinzu und importieren Sie ihn.
Ich möchte ein Spiel mit Python machen
Ich möchte ein Element mit numpy in eine Datei schreiben und es überprüfen.
Ein Anfänger des maschinellen Lernens versuchte, mit Python ein Vorhersagemodell für Pferderennen zu erstellen
Ich habe versucht, mit dem Seq2Seq-Modell von TensorFlow so etwas wie einen Chatbot zu erstellen
Ich habe versucht, mit Open AI Gym eine verbesserte Lernumgebung für Othello zu schaffen
Anfänger des maschinellen Lernens versuchen, einen Entscheidungsbaum zu erstellen
So zeichnen Sie interaktiv eine Pipeline für maschinelles Lernen mit scikit-learn und speichern sie in HTML
Ich möchte einen Sprachwechsler mit Python und SPTK in Bezug auf eine berühmte Site erstellen
Verknüpfen Sie Python Enum mit einer Funktion, um es aufrufbar zu machen
Experimentieren Sie mit Python, um ein PDF für Selbstversorger für Kindle zu erstellen
Ich möchte ein Klickmakro mit pyautogui (Wunsch) erstellen.
Für diejenigen, die mit TensorFlow2 maschinelles Lernen beginnen möchten
Ich möchte ein Klickmakro mit pyautogui (Outlook) erstellen.
Bibliothek zur Angabe eines Nameservers in Python und Dig
PyPI-Registrierungsverfahren für diejenigen, die ihr PyPI-Debüt geben möchten
So erstellen Sie eine Überwachungskamera (Überwachungskamera) mit Opencv und Python
Machen Sie ein Thermometer mit Raspberry Pi und machen Sie es im Browser Teil 4 sichtbar
Ich habe versucht, mit Selenium und Python einen regelmäßigen Ausführungsprozess durchzuführen
Ich möchte eine Pipfile erstellen und im Docker wiedergeben
Wirf etwas mit Python in Kinesis und stelle sicher, dass es drin ist
Versuchen Sie, eine Blackjack-Strategie zu entwickeln, indem Sie das Lernen stärken ((1) Implementierung von Blackjack)
Ich habe versucht, Jojo mit LSTM ein seltsames Zitat zu machen
[Konzept] Strategie zur Analyse von Daten mit Python und zum Ziel eines Rückgangs nach den Vorteilen für die Aktionäre
Ist es möglich, ein Pre-Listing-Unternehmen zu gründen und mit Aktienoptionen ein Vermögen zu machen?
[Hi Py (Teil 1)] Ich möchte vorerst etwas machen, also setze zuerst ein Ziel.
TF2RL: Erweiterte Lernbibliothek für TensorFlow2.x
<Für Anfänger> Python-Bibliothek <Für maschinelles Lernen>
2. Erstellen Sie mit Python einen Entscheidungsbaum von 0 und verstehen Sie ihn (2. Grundlagen des Python-Programms)
Python-Anfänger haben beschlossen, einen LINE-Bot mit Flask zu erstellen (Flask-Kommentar)
Eine Sammlung von Tipps zur Beschleunigung des Lernens und Denkens mit PyTorch
So machen Sie den Containernamen in Docker als Subdomain zugänglich
Bedeutet Memo, wenn versucht wird, maschinelles Lernen mit 50 Bildern durchzuführen
Machen Sie mit Python einen Entscheidungsbaum von 0 und verstehen Sie ihn (4. Datenstruktur)
Ich möchte eine Webanwendung mit React und Python Flask erstellen
Ich dachte, ich könnte einen netten Gitignore-Editor machen, also habe ich vorerst versucht, so etwas wie MVP zu machen