Hi guys… I encountered a super weird problem. I have been stuck for days… Please help
So I have two properties for my character, PowerLevel, and BaseSpeed:
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Power)
float PowerLevel;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category=Power)
float BaseSpeed;
I assign them default values in the class constructor:
PowerLevel = 100.f;
BaseSpeed = 5.f;
I also log the memory address of the this pointer and the two properties (you will see why later):
//ATest1Character.cpp
UE_LOG(LogTemp, Warning, TEXT("this start:%p"), (void *)this);
UE_LOG(LogTemp, Warning, TEXT("powerlevel ptr start: %p"), (void *)&PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("powerlevel start: %f"), PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("basespeed ptr start: %p"), (void *)&BaseSpeed);
UE_LOG(LogTemp, Warning, TEXT("basespeed start: %f"), BaseSpeed);
Now, I want to modify PowerLevel and BaseSpeed from a sqlite table called player. So I get the respective PowerLevel and BaseSpeed from the table and assign them to these variables. However, they seem to reset back to those default values, even tho PowerLevel and BaseSpeed BOTH have the new values after the assignment!
Please see the following:
I use the function sqlite3_exec, which executes an sql statement and returns the value in a callback function.
Sqlite is written in c, so the callback function has the following signature:
(void *, int, char**, char**);
With the following signature, we cannot pass a normal class method, since normal class methods passes THIS as a implicit parameter. However, a static class method does not have a this pointer, so I tried to do a workaround by passing that as the callback:
static int StaticGetStats(void*, int, char**, char**);
and my sql_exec function call:
sqlite3 *db;
char *zErrMsg = 0;
int rc;
char * sql;
rc = sqlite3_open("db/pstats.db", &db);
if (GEngine)
{
if (rc == SQLITE_OK){
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Yellow, TEXT("Connection Opened! Yay!"));
sql = "Select BasePowerLevel, BaseSpeed from player where name = \"player1\"";
rc = sqlite3_exec(db, sql, ATest1Character::StaticGetStats, this, &zErrMsg);
As you can see, I pass the THIS pointer when calling sqlite3_exec. This is because sqlite3_exec passes the THIS pointer as a void* in the first argument of the callback function - In this case, the first argument in ATest1Character::StaticGetStats.
so in my StaticGetStats body definition, since I have a pointer to the instance that called it, I can cast the void* back to a ATest1Character*, and call the NORMAL NON STATIC GetStats class method of the instance:
int ATest1Character::StaticGetStats(void *me, int columns, char **columnvalues, char **columnnames){
UE_LOG(LogTemp, Warning, TEXT("Static me ptr is:%p"), (void *)me);
TArray<FString> UColumnValues;
TArray<FString> UColumnNames;
for (int i = 0; i < columns; i++){
FString fcolumnvalue(columnvalues[i]);
UE_LOG(LogTemp, Warning, TEXT("columnvalue is:%s"), *fcolumnvalue);
UColumnValues.Add(fcolumnvalue);
FString fcolumnnames(columnnames[i]);
UColumnNames.Add(fcolumnnames);
}
ATest1Character* Myself = static_cast<ATest1Character*>(me);
if (Myself != NULL)
if (sizeof(columns) == 4){
int32 UColumns = columns;
Myself->GetStats(UColumns, UColumnValues, UColumnNames);
}
return 0;
}
Finally, in my class method GetStats, I get the values I need from the table and assign them to my PowerLevel and BaseSpeed:
void ATest1Character::GetStats(int32 UColumns, TArray<FString> UColumnValues, TArray<FString> UColumnNames){
if (GEngine){
UE_LOG(LogTemp, Warning, TEXT("this after:%p"), (void *)this);
for (int i = 0; i < UColumns; i++){
UE_LOG(LogTemp, Warning, TEXT("UColumnValue is %s"), *UColumnValues[i]);
GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, UColumnValues[i]);
GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, TEXT(" = "));
GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, UColumnNames[i]);
GEngine->AddOnScreenDebugMessage(-1, 50.f, FColor::Yellow, TEXT("\n"));
if (UColumnNames[i] == "BasePowerLevel"){
float pl = FCString::Atof(*UColumnValues[i]); //value should be 2000
PowerLevel = pl;
UE_LOG(LogTemp, Warning, TEXT("powerlevel ptr after: %p"), (void *)&PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("powerlevel after: %f"), PowerLevel);
}
if (UColumnNames[i] == "BaseSpeed"){
float bs = FCString::Atof(*UColumnValues[i]); //value should be 10
BaseSpeed = bs;
UE_LOG(LogTemp, Warning, TEXT("basespeed ptr after: %p"), (void *)&BaseSpeed);
UE_LOG(LogTemp, Warning, TEXT("basespeed after: %f"), BaseSpeed);
}
}
}
}
In the Tick function of my character, I print to the log the current address and value of PowerLevel and BaseSpeed:
void ATest1Character::Tick(float DeltaSeconds){
Super::Tick(DeltaSeconds);
UE_LOG(LogTemp, Warning, TEXT("Powerlevel Tick: %f"), PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("PowerLevel ptr Tick: %p"), &PowerLevel);
UE_LOG(LogTemp, Warning, TEXT("BaseSpeed Tick: %f"), BaseSpeed);
UE_LOG(LogTemp, Warning, TEXT("BaseSpeed ptr Tick: %p"), &BaseSpeed);
CharacterMovement->MaxWalkSpeed = SpeedFactor * PowerLevel + BaseSpeed;
}
When I run the game, in my character’s constructor, my character’s PowerLevel and BaseSpeed are supposed to be updated by accessing the table. However, it doesn’t happen.
I instantly thought that perhaps I was creating a copy of the character and setting the variables in the copy. So i logged the memory address of the THIS pointer and the PowerLevel and BaseSpeed properties before the sqlite call and after it was made. But the log shows that they are the SAME memory addresses:
//Log
LogTemp:Warning: this start:000000D9177B6400
LogTemp:Warning: powerlevel ptr start: 000000D9177B6918
LogTemp:Warning: powerlevel start: 100.000000
LogTemp:Warning: basespeed ptr start: 000000D9177B6920
LogTemp:Warning: basespeed start: 5.000000
LogTemp:Warning: Static me ptr is:000000D9177B6400
LogTemp:Warning: columnvalue is:2000.0
LogTemp:Warning: columnvalue is:10.0
LogTemp:Warning: this after:000000D9177B6400
LogTemp:Warning: UColumnValue is 2000.0
LogTemp:Warning: powerlevel ptr after: 000000D9177B6918
LogTemp:Warning: powerlevel after: 2000.000000
LogTemp:Warning: UColumnValue is 10.0
LogTemp:Warning: basespeed ptr after: 000000D9177B6920
LogTemp:Warning: basespeed after: 10.000000
PIE: Info Play in editor start time for /Game/Maps/UEDPIE_0_Example_Map -0.712
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920
LogTemp:Warning: Powerlevel Tick: 100.000000
LogTemp:Warning: PowerLevel ptr Tick: 000000D9177B6918
LogTemp:Warning: BaseSpeed Tick: 5.000000
LogTemp:Warning: BaseSpeed ptr Tick: 000000D9177B6920
Now, if I try to assign values to PowerLevel and BaseSpeed in a normal class method, the assignment works perfectly. Does anyone have any idea whats going on here? I have been stuck on this issue for days, and I simply cannot understand how this problem can exist. Is there something going on behind the scenes in unreal engine that’s resetting the properties?