Skip to content

Commit dddbe87

Browse files
author
Roberto De Ioris
committed
added preliminary navigation support
1 parent fa58ace commit dddbe87

File tree

3 files changed

+291
-0
lines changed

3 files changed

+291
-0
lines changed

Source/UnrealEnginePython/Private/UEPyModule.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,47 @@ static PyObject *py_ue_quit_game(ue_PyUObject *self, PyObject * args) {
677677
return Py_None;
678678
}
679679

680+
static PyObject *py_ue_simple_move_to_location(ue_PyUObject *self, PyObject * args) {
681+
682+
ue_py_check(self);
683+
684+
float x, y, z;
685+
if (!PyArg_ParseTuple(args, "fff:simple_move_to_location", &x, &y, &z)) {
686+
return NULL;
687+
}
688+
689+
UWorld *world = ue_get_uworld(self);
690+
if (!world)
691+
return PyErr_Format(PyExc_Exception, "unable to retrieve UWorld from uobject");
692+
693+
APawn *pawn = nullptr;
694+
695+
if (self->ue_object->IsA<APawn>()) {
696+
pawn = (APawn *)self->ue_object;
697+
}
698+
else if (self->ue_object->IsA<UActorComponent>()) {
699+
UActorComponent *component = (UActorComponent *)self->ue_object;
700+
AActor *actor = component->GetOwner();
701+
if (actor) {
702+
if (actor->IsA<APawn>()) {
703+
pawn = (APawn *)actor;
704+
}
705+
}
706+
}
707+
708+
if (!pawn)
709+
return PyErr_Format(PyExc_Exception, "uobject is not a pawn");
710+
711+
AController *controller = pawn->GetController();
712+
if (!controller)
713+
return PyErr_Format(PyExc_Exception, "Pawn has no controller");
714+
715+
world->GetNavigationSystem()->SimpleMoveToLocation(controller, FVector(x, y, z));
716+
717+
Py_INCREF(Py_None);
718+
return Py_None;
719+
}
720+
680721
static PyObject *py_ue_get_world(ue_PyUObject *self, PyObject * args) {
681722

682723
ue_py_check(self);
@@ -1240,6 +1281,7 @@ static PyMethodDef ue_PyUObject_methods[] = {
12401281
{ "get_class", (PyCFunction)py_ue_get_class, METH_VARARGS, "" },
12411282
{ "actor_components", (PyCFunction)py_ue_actor_components, METH_VARARGS, "" },
12421283
{ "quit_game", (PyCFunction)py_ue_quit_game, METH_VARARGS, "" },
1284+
{ "simple_move_to_location", (PyCFunction)py_ue_simple_move_to_location, METH_VARARGS, "" },
12431285
{ "is_input_key_down", (PyCFunction)py_ue_is_input_key_down, METH_VARARGS, "" },
12441286
{ "actor_has_component_of_type", (PyCFunction)py_ue_actor_has_component_of_type, METH_VARARGS, "" },
12451287
{ "actor_destroy", (PyCFunction)py_ue_actor_destroy, METH_VARARGS, "" },
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#include "UnrealEnginePythonPrivatePCH.h"
2+
#include "PyPawn.h"
3+
4+
5+
6+
APyPawn::APyPawn()
7+
{
8+
PrimaryActorTick.bCanEverTick = true;
9+
10+
this->OnActorBeginOverlap.AddDynamic(this, &APyPawn::PyOnActorBeginOverlap);
11+
this->OnClicked.AddDynamic(this, &APyPawn::PyOnActorClicked);
12+
13+
// pre-generate PyUObject (for performance)
14+
ue_get_python_wrapper(this);
15+
}
16+
17+
18+
// Called when the game starts
19+
void APyPawn::BeginPlay()
20+
{
21+
Super::BeginPlay();
22+
23+
// ...
24+
25+
const TCHAR *blob = *PythonCode;
26+
27+
int ret = PyRun_SimpleString(TCHAR_TO_UTF8(blob));
28+
29+
UE_LOG(LogPython, Warning, TEXT("Python ret = %d"), ret);
30+
31+
if (ret) {
32+
unreal_engine_py_log_error();
33+
}
34+
35+
if (PythonModule.IsEmpty())
36+
return;
37+
38+
PyObject *py_pawn_module = PyImport_ImportModule(TCHAR_TO_UTF8(*PythonModule));
39+
if (!py_pawn_module) {
40+
unreal_engine_py_log_error();
41+
return;
42+
}
43+
44+
#if WITH_EDITOR
45+
// todo implement autoreload with a dictionary of module timestamps
46+
py_pawn_module = PyImport_ReloadModule(py_pawn_module);
47+
if (!py_pawn_module) {
48+
unreal_engine_py_log_error();
49+
return;
50+
}
51+
#endif
52+
53+
if (PythonClass.IsEmpty())
54+
return;
55+
56+
PyObject *py_pawn_module_dict = PyModule_GetDict(py_pawn_module);
57+
PyObject *py_pawn_class = PyDict_GetItemString(py_pawn_module_dict, TCHAR_TO_UTF8(*PythonClass));
58+
59+
if (!py_pawn_class) {
60+
unreal_engine_py_log_error();
61+
return;
62+
}
63+
64+
py_pawn_instance = PyObject_CallObject(py_pawn_class, NULL);
65+
if (!py_pawn_instance) {
66+
unreal_engine_py_log_error();
67+
return;
68+
}
69+
70+
PyObject *py_obj = (PyObject *) ue_get_python_wrapper(this);
71+
72+
if (py_obj) {
73+
PyObject_SetAttrString(py_pawn_instance, "uobject", py_obj);
74+
}
75+
else {
76+
UE_LOG(LogPython, Error, TEXT("Unable to set 'uobject' field in pawn wrapper class"));
77+
}
78+
79+
PyObject *bp_ret = PyObject_CallMethod(py_pawn_instance, "begin_play", NULL);
80+
if (!bp_ret) {
81+
unreal_engine_py_log_error();
82+
return;
83+
}
84+
Py_DECREF(bp_ret);
85+
}
86+
87+
88+
// Called every frame
89+
void APyPawn::Tick(float DeltaTime)
90+
{
91+
Super::Tick(DeltaTime);
92+
93+
if (!py_pawn_instance)
94+
return;
95+
96+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, "tick", "f", DeltaTime);
97+
if (!ret) {
98+
unreal_engine_py_log_error();
99+
return;
100+
}
101+
Py_DECREF(ret);
102+
103+
}
104+
105+
106+
void APyPawn::PyOnActorBeginOverlap(AActor *overlapped, AActor *other)
107+
{
108+
if (!py_pawn_instance)
109+
return;
110+
111+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, "on_actor_begin_overlap", "O", (PyObject *)ue_get_python_wrapper(other));
112+
if (!ret) {
113+
unreal_engine_py_log_error();
114+
return;
115+
}
116+
Py_DECREF(ret);
117+
}
118+
119+
void APyPawn::PyOnActorClicked(AActor *touched_actor, FKey button_pressed)
120+
{
121+
if (!py_pawn_instance)
122+
return;
123+
124+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, "on_actor_clicked", "Os", (PyObject *)ue_get_python_wrapper(touched_actor), TCHAR_TO_UTF8(*button_pressed.ToString()));
125+
if (!ret) {
126+
unreal_engine_py_log_error();
127+
return;
128+
}
129+
Py_DECREF(ret);
130+
}
131+
132+
void APyPawn::CallPythonPawnMethod(FString method_name)
133+
{
134+
if (!py_pawn_instance)
135+
return;
136+
137+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, TCHAR_TO_UTF8(*method_name), NULL);
138+
if (!ret) {
139+
unreal_engine_py_log_error();
140+
return;
141+
}
142+
Py_DECREF(ret);
143+
}
144+
145+
bool APyPawn::CallPythonPawnMethodBool(FString method_name)
146+
{
147+
if (!py_pawn_instance)
148+
return false;
149+
150+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, TCHAR_TO_UTF8(*method_name), NULL);
151+
if (!ret) {
152+
unreal_engine_py_log_error();
153+
return false;
154+
}
155+
156+
if (PyObject_IsTrue(ret)) {
157+
Py_DECREF(ret);
158+
return true;
159+
}
160+
161+
Py_DECREF(ret);
162+
return false;
163+
}
164+
165+
FString APyPawn::CallPythonPawnMethodString(FString method_name)
166+
{
167+
if (!py_pawn_instance)
168+
return FString();
169+
170+
PyObject *ret = PyObject_CallMethod(py_pawn_instance, TCHAR_TO_UTF8(*method_name), NULL);
171+
if (!ret) {
172+
unreal_engine_py_log_error();
173+
return FString();
174+
}
175+
176+
PyObject *py_str = PyObject_Str(ret);
177+
if (!py_str) {
178+
Py_DECREF(ret);
179+
return FString();
180+
}
181+
182+
char *str_ret = PyUnicode_AsUTF8(py_str);
183+
184+
FString ret_fstring = FString(UTF8_TO_TCHAR(str_ret));
185+
186+
Py_DECREF(py_str);
187+
188+
return ret_fstring;
189+
}
190+
191+
192+
APyPawn::~APyPawn()
193+
{
194+
Py_XDECREF(py_pawn_instance);
195+
UE_LOG(LogPython, Warning, TEXT("Python APawn wrapper XDECREF'ed"));
196+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
4+
#include "GameFramework/Pawn.h"
5+
6+
#include "PyPawn.generated.h"
7+
8+
9+
10+
UCLASS(BlueprintType, Blueprintable)
11+
class APyPawn : public APawn
12+
{
13+
GENERATED_BODY()
14+
15+
public:
16+
// Sets default values for this component's properties
17+
APyPawn();
18+
~APyPawn();
19+
20+
// Called when the game starts
21+
virtual void BeginPlay() override;
22+
23+
// Called every frame
24+
virtual void Tick(float DeltaSeconds) override;
25+
26+
UPROPERTY(EditAnywhere, meta = (Multiline = true), Category = "Python")
27+
FString PythonCode;
28+
29+
UPROPERTY(EditAnywhere , Category = "Python")
30+
FString PythonModule;
31+
32+
UPROPERTY(EditAnywhere, Category = "Python")
33+
FString PythonClass;
34+
35+
UFUNCTION(BlueprintCallable, Category = "Python")
36+
void CallPythonPawnMethod(FString method_name);
37+
38+
UFUNCTION(BlueprintCallable, Category = "Python")
39+
bool CallPythonPawnMethodBool(FString method_name);
40+
41+
UFUNCTION(BlueprintCallable, Category = "Python")
42+
FString CallPythonPawnMethodString(FString method_name);
43+
44+
private:
45+
PyObject *py_pawn_instance;
46+
47+
UFUNCTION()
48+
void PyOnActorBeginOverlap(AActor *overlapped, AActor *other);
49+
50+
UFUNCTION()
51+
void PyOnActorClicked(AActor *touched_actor, FKey button_pressed);
52+
};
53+

0 commit comments

Comments
 (0)