PDA

View Full Version : Getting player's position from a custom Kismet event?!



Afr0
05-15-2010, 10:57 AM
Can somebody please get this straight once and for all?
How do you get the player's (actor) pawn's position in the current level from a custom Kismet event?!

I have tried using:



PlayerLocation = LocalPlayer.Location;


and:



PlayerLocation = LocalPlayer.WorldInfo.GetALocalPlayerController(). Location;


where PlayerLocation is a Vector and LocalPlayer is an Actor that has been initialized with:


VariableLinks.Add((ExpectedType=class'Engine.SeqVa r_Object',LinkDesc="Player",PropertyName=LocalPlayer,MaxVars=1))

Both of these seem to return the exact same coordinates regardless of where I am in the level. So.... how do I get the player's coordinates?!

Wizzard~Of~Ozz
05-15-2010, 11:08 AM
LocalPlayer.Actor.Pawn.Location ?

Afr0
05-15-2010, 11:14 AM
LocalPlayer already is an Actor, and it doesn't seem to have a Pawn member... o_O

Edit: Oh yeah, and I checked - Seqvar_Object doesn't have an Actor member

Wizzard~Of~Ozz
05-15-2010, 11:23 AM
LocalPlayer extends Player, Player has a variable called Actor which is the Playercontroller.

// The actor this player controls.
var transient const playercontroller Actor;

PlayerController has a variable through superclass called Pawn.
/** Pawn currently being controlled by this controller. Use Pawn.Possess() to take control of a pawn */
var repnotify Pawn Pawn;


Gotta love some ambiguity in variable names.
I assumed if you were already calling LocalPlayer it was available.

Afr0
05-15-2010, 11:37 AM
Hmm.
I declared LocalPlayer to be an instance of the Player class.
Now I got the Actor and Actor.Pawn members. :)

The problem now is that the coordinates returned are 0,0 and 0.
And Player.Actor.WorldInfo.Title returns squat.

This... kind of makes sense, I think.
I am calling the event when I'm pressing a button in a custom MidGameMenu. I think this is a UIScene and thus it has no notion of the player's position in the actual game-level.

So... erm.... yeah.

Any ideas? :(

Wizzard~Of~Ozz
05-15-2010, 11:57 AM
If you create an instance, it will be @ 0,0,0. You need to get the instance.

Why not use GetLocationAndRotation Action already in Kismet?



Begin Object Class=SeqEvent_PlayerSpawned Name=SeqEvent_PlayerSpawned_0
MaxWidth=201
OutputLinks(0)=(DrawY=674)
VariableLinks(0)=(LinkedVariables=(SeqVar_Player'S eqVar_Player_0'),DrawX=110)
VariableLinks(1)=(DrawX=202)
ObjInstanceVersion=1
ParentSequence=Sequence'Main_Sequence'
ObjPosX=48
ObjPosY=608
DrawWidth=123
DrawHeight=123
Name="SeqEvent_PlayerSpawned_0"
ObjectArchetype=SeqEvent_PlayerSpawned'Engine.Defa ult__SeqEvent_PlayerSpawned'
End Object
Begin Object Class=SeqVar_Player Name=SeqVar_Player_0
ObjInstanceVersion=1
ParentSequence=Sequence'Main_Sequence'
ObjPosX=312
ObjPosY=888
DrawWidth=32
DrawHeight=32
Name="SeqVar_Player_0"
ObjectArchetype=SeqVar_Player'Engine.Default__SeqV ar_Player'
End Object
Begin Object Class=SeqAct_GetLocationAndRotation Name=SeqAct_GetLocationAndRotation_0
InputLinks(0)=(DrawY=634)
OutputLinks(0)=(DrawY=634)
VariableLinks(0)=(LinkedVariables=(SeqVar_Player'S eqVar_Player_0'),DrawX=781)
VariableLinks(1)=(DrawX=864)
VariableLinks(2)=(DrawX=956)
ObjInstanceVersion=2
ParentSequence=Sequence'Main_Sequence'
ObjPosX=736
ObjPosY=600
DrawWidth=274
DrawHeight=80
Name="SeqAct_GetLocationAndRotation_0"
ObjectArchetype=SeqAct_GetLocationAndRotation'Engi ne.Default__SeqAct_GetLocationAndRotation'
End Object

Afr0
05-15-2010, 12:07 PM
I didn't create an instance?

I just changed it's type...



var() Player LocalPlayer;


and then in defaultproperties:

defaultproperties
{
ObjName="SaveEvent"
ObjCategory="Misc"

InputLinks.Empty
InputLinks.Add((LinkDesc="Activate"))

VariableLinks.Empty
VariableLinks.Add((ExpectedType=class'Engine.SeqVa r_Object',LinkDesc="Player",PropertyName=LocalPlayer,MaxVars=1))
}

Wizzard~Of~Ozz
05-15-2010, 12:51 PM
Well, aside from reinventing the wheel, I wrote this up which does work as a standalone.



class MySeqAct_GetLocation extends SequenceAction;

/** The location of the actor */
var() editconst Vector Location;

var Object Target;

function Activated()
{
if( Pawn( Target ) != None )
Location = Pawn( Target ).Location;
else if ( Controller ( Target ) != None )
Location = Controller( Target ).Pawn.Location;
}

defaultproperties
{
ObjName="Get Player Location"
ObjCategory="Custom"
ObjColor=(R=255,G=0,B=255,A=255)
VariableLinks(0)=(ExpectedType=class'SeqVar_Object ',LinkDesc="Target Player",PropertyName=Target)
VariableLinks(1)=(ExpectedType=class'SeqVar_Vector ',LinkDesc="Location",PropertyName=Location, bWriteable=true)
}

Afr0
05-15-2010, 02:08 PM
Wow!

Thank you so much! :)

Here's the final code I ended up with (feel free to copypasta from this anyone):


class LarryLoadEvent extends SequenceEvent DLLBind(Sex);

struct Save
{
//var string LevelName;
var float X, Y, Z;
};

var() Object LocalPlayer;
var() Vector PlayerLocation;
var() string SaveFile;
var() Save ReturnedSave;

dllimport final function Save LoadGame(string LoadPath);
dllimport final function string LoadDiag();

event Activated()
{
SaveFile = LoadDiag();
ReturnedSave = LoadGame(SaveFile);

//`log(ReturnedSave.LevelName);
`log(ReturnedSave.X);
`log(ReturnedSave.Y);
`log(ReturnedSave.Z);

PlayerLocation.X = ReturnedSave.X;
PlayerLocation.Y = ReturnedSave.Y;
PlayerLocation.Z = ReturnedSave.Z;

if(Pawn(LocalPlayer) != none)
Pawn(LocalPlayer).SetLocation(PlayerLocation);
else if(Controller(LocalPlayer) != none)
Controller(LocalPlayer).Pawn.SetLocation(PlayerLoc ation);

`log("Event activated!");
}

event bool IsValidUISequenceObject(optional UIScreenObject TargetObject)
{
return true;
}

defaultproperties
{
ObjName="LoadEvent"
ObjCategory="Misc"

InputLinks.Empty
InputLinks.Add((LinkDesc="Activate"))

VariableLinks.Empty
VariableLinks.Add((ExpectedType=class'Engine.SeqVa r_Object',LinkDesc="Player",PropertyName=LocalPlayer,MaxVars=1))
}


class LarrySaveEvent extends SequenceEvent DLLBind(Sex) dependson(LarryPlayerController);

var Object LocalPlayer;
var() Vector PlayerLocation;
var() string SaveFile;

dllimport final function bool SaveGame(string LevelName, float X, float Y, float Z, string SavePath);
dllimport final function string SaveDiag();

event Activated()
{
local string LevelName;

if(Pawn(LocalPlayer) != none)
{
PlayerLocation = Pawn(LocalPlayer).Location;
LevelName = Pawn(LocalPlayer).WorldInfo.Title;
}
else if(Controller(LocalPlayer) != none)
{
PlayerLocation = Controller(LocalPlayer).Pawn.Location;
LevelName = Controller(LocalPlayer).Pawn.WorldInfo.Title;
}

SaveFile = SaveDiag();
`log(SaveFile);
SaveGame(LevelName, PlayerLocation.X, PlayerLocation.Y, PlayerLocation.Z, SaveFile);

`log("Event activated!");
}

event bool IsValidUISequenceObject(optional UIScreenObject TargetObject)
{
return true;
}

defaultproperties
{
ObjName="SaveEvent"
ObjCategory="Misc"

InputLinks.Empty
InputLinks.Add((LinkDesc="Activate"))

VariableLinks.Empty
VariableLinks.Add((ExpectedType=class'Engine.SeqVa r_Object',LinkDesc="Player",PropertyName=LocalPlayer,MaxVars=1))
}

Edit: Turns out I had just made a rudimentary mistake in my C++ code when reading the save file. Here is the updated code:


// SaveDLL.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"
#include "OSFDlg.h"
#include <fstream>
#include <iostream>
#include <sstream>

using namespace std;

template <typename Target, typename Source>
Target lexical_cast(Source source)
{
Target target;

std::wostringstream os;
os << source;
std::wistringstream is(os.str());
is >> target;

return target;
}

extern "C"
{
struct Save
{
//wchar_t *LevelName;
float X, Y, Z;
};

wchar_t* ReadLine(wifstream *Stream)
{
wchar_t *Line = new wchar_t[256];
wchar_t ControlChar = '0';
int i = 0;

while(ControlChar != '\n')
{
ControlChar = Stream->get();
cout << ControlChar;
Line[i] = ControlChar;

i++;

if(i == 256)
break;
}

return Line;
}

__declspec(dllexport) unsigned int SaveGame(wchar_t* LevelName, float X, float Y, float Z, wchar_t* SavePath)
{
wofstream SaveStream(SavePath, ios::out);
SaveStream << LevelName;
SaveStream << std::endl;
SaveStream << X;
SaveStream << std::endl;
SaveStream << Y;
SaveStream << std::endl;
SaveStream << Z;
SaveStream << std::endl;
SaveStream.close();

return 1;
}

__declspec(dllexport) Save* LoadGame(wchar_t* LoadPath)
{
wifstream LoadStream(LoadPath, ios::in);
wchar_t *LevelName = new wchar_t[100];
wchar_t *X = new wchar_t[100];
wchar_t *Y = new wchar_t[100];
wchar_t *Z = new wchar_t[100];

static Save NewSave;

LevelName = new wchar_t[100];
LevelName = ReadLine(&LoadStream);

X = ReadLine(&LoadStream);
NewSave.X = lexical_cast<float, wchar_t[]>(X);

Y = ReadLine(&LoadStream);
NewSave.Y = lexical_cast<float, wchar_t[]>(Y);

Z = ReadLine(&LoadStream);
NewSave.Z = lexical_cast<float, wchar_t[]>(Z);

LoadStream.close();

/*wstringstream WSS;
WSS << NewSave.X;

MessageBox(NULL, (LPCWSTR)WSS.str().c_str(), TEXT("LoadGame()"), MB_OKCANCEL);*/

delete X;
delete Y;
delete Z;

return &NewSave;
}

__declspec(dllexport) wchar_t *SaveDiag()
{
//Filter
TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
//Default Extension
TCHAR szDefExtension[] = TEXT("sav\0");

static COSFDialog SaveDiag;
static wchar_t* Filename;

if(SaveDiag.FileSaveDlg(szFilter, szDefExtension, TEXT("Save Game")))
{
Filename = (wchar_t*)SaveDiag.GetFileName();
return Filename;
}
}

__declspec(dllexport) wchar_t *LoadDiag()
{
//Filter
TCHAR szFilter[] = TEXT("Save Files (*.SAV)\0*.sav\0");
//Default Extension
TCHAR szDefExtension[] = TEXT("sav\0");

static COSFDialog LoadDiag;
static wchar_t* Filename;

if(LoadDiag.FileOpenDlg(szFilter, szDefExtension, TEXT("Load Game"), false))
{
Filename = (wchar_t*)LoadDiag.GetFileName();
return Filename;
}
}
}


Warning: Doesn't really have any error checking and is basically a hacked-together mess. But it works. :)
Also, notice that if you're going to load a specific level (which you should do before placing the player at a given position), you need to implement a separate C++ function for that, because Unreal doesn't like you returning structs with strings in them. It's taken me a month to figure out that....

Afr0
05-16-2010, 12:23 PM
Ok, so I made a quick function in C++ to load a level's name.
The level seems to load nicely in UDK using the 'Open' consolecommand, but when I try to set the player's position afterwards, it doesn't work! :(


if(Pawn(LocalPlayer) != none)
{
Pawn(LocalPlayer).WorldInfo.ConsoleCommand("Open " $LevelName, true);
Pawn(LocalPlayer).SetLocation(PlayerLocation);
}
else if(Controller(LocalPlayer) != none)
{
Controller(LocalPlayer).Pawn.WorldInfo.ConsoleComm and("Open " $LevelName, true);
Controller(LocalPlayer).Pawn.SetLocation(PlayerLoc ation);
}

Is there another way of loading a level besides using a console command, and how do you set a player's position after a level has been loaded?

Afr0
05-16-2010, 09:40 PM
Bump, bump!

Afr0
05-31-2010, 04:30 AM
Honestly, is there no way of setting a player's position after a level has been loaded? :\

mightyenigma
01-08-2012, 04:24 AM
Is SetLocation not working?

UnrealEverything
01-08-2012, 08:04 AM
You need to wait until the new map is loaded until you can set location (in other words, wait for level loaded and then setlocation)