mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
418 lines
10 KiB
C++
418 lines
10 KiB
C++
/**
|
|
|
|
GDevelop - Path Behavior Extension
|
|
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
|
|
This project is released under the MIT License.
|
|
*/
|
|
|
|
#include <memory>
|
|
#include <cmath>
|
|
#include "PathBehavior.h"
|
|
#include "PathBehaviorEditor.h"
|
|
#include "GDCpp/Runtime/Project/Layout.h"
|
|
#include "GDCpp/Runtime/Serialization/SerializerElement.h"
|
|
#include "GDCpp/Runtime/RuntimeScene.h"
|
|
#include "GDCpp/Runtime/RuntimeObject.h"
|
|
#include "GDCpp/Runtime/CommonTools.h"
|
|
#include "RuntimeScenePathDatas.h"
|
|
|
|
PathBehavior::PathBehavior() :
|
|
speed(200),
|
|
timeOnSegment(0),
|
|
totalSegmentTime(0),
|
|
currentSegment(0),
|
|
stopAtEnd(false),
|
|
reverseAtEnd(false),
|
|
followAngle(false),
|
|
angleOffset(0),
|
|
isPathLoaded(false),
|
|
futureSegment(-1),
|
|
futurePosition(-1),
|
|
runtimeScenesPathDatas(NULL)
|
|
{
|
|
Reset();
|
|
SetPath("Object main path", std::vector<sf::Vector2f>(1, sf::Vector2f(0,0)));
|
|
ChangeCurrentPath("Object main path");
|
|
}
|
|
|
|
PathBehavior::PathBehavior(const PathBehavior &cl) : Behavior(cl)
|
|
{
|
|
Init(cl);
|
|
}
|
|
|
|
PathBehavior& PathBehavior::operator=(const PathBehavior &cl)
|
|
{
|
|
Init(cl);
|
|
return *this;
|
|
}
|
|
|
|
void PathBehavior::Init(const PathBehavior &cl)
|
|
{
|
|
name = cl.name;
|
|
type = cl.type;
|
|
|
|
isPathLoaded = false;
|
|
|
|
pathName = cl.pathName;
|
|
path = cl.path;
|
|
|
|
offset = cl.offset;
|
|
speed = cl.speed;
|
|
timeOnSegment = cl.timeOnSegment;
|
|
totalSegmentTime = cl.totalSegmentTime;
|
|
currentSegment = cl.currentSegment;
|
|
|
|
stopAtEnd = cl.stopAtEnd;
|
|
reverseAtEnd = cl.reverseAtEnd;
|
|
|
|
followAngle = cl.followAngle;
|
|
angleOffset = cl.angleOffset;
|
|
|
|
localePaths = cl.localePaths;
|
|
|
|
runtimeScenesPathDatas = cl.runtimeScenesPathDatas;
|
|
}
|
|
|
|
void PathBehavior::Reset()
|
|
{
|
|
EnterSegment(0);
|
|
}
|
|
|
|
void PathBehavior::EnterSegment(std::size_t segmentNumber)
|
|
{
|
|
currentSegment = segmentNumber;
|
|
if (!path.empty() && currentSegment < path.size()-1)
|
|
{
|
|
sf::Vector2f newPath = (path[currentSegment + 1] - path[currentSegment]);
|
|
totalSegmentTime = sqrtf(newPath.x*newPath.x+newPath.y*newPath.y);
|
|
timeOnSegment = 0;
|
|
|
|
if(followAngle)
|
|
object->SetAngle(GetAngleOfSegment(newPath) + angleOffset);
|
|
}
|
|
}
|
|
|
|
PathBehavior::~PathBehavior()
|
|
{
|
|
}
|
|
|
|
#if defined(GD_IDE_ONLY)
|
|
void PathBehavior::EditBehavior( wxWindow* parent, gd::Project & game_, gd::Layout * scene, gd::MainFrameWrapper & mainFrameWrapper_ )
|
|
{
|
|
#if !defined(GD_NO_WX_GUI)
|
|
PathBehaviorEditor editor(parent, game_, scene, *this, mainFrameWrapper_);
|
|
editor.ShowModal();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* Called at each frame before events :
|
|
* Position the object on the path
|
|
*/
|
|
void PathBehavior::DoStepPreEvents(RuntimeScene & scene)
|
|
{
|
|
if(!isPathLoaded)
|
|
{
|
|
LoadPath(scene);
|
|
Reset();
|
|
}
|
|
|
|
// add to the current time along the path
|
|
timeOnSegment += static_cast<double>(scene.GetTimeManager().GetElapsedTime())
|
|
/ 1000000.0 * speed;
|
|
|
|
// if I reached the end of this segment, move to a new segment
|
|
if (timeOnSegment >= totalSegmentTime && currentSegment < path.size())
|
|
EnterSegment(currentSegment + 1);
|
|
|
|
//Position object on the segment
|
|
sf::Vector2f newPos;
|
|
if ( !path.empty() && currentSegment < path.size()-1 )
|
|
newPos = offset + path[currentSegment] + (path[currentSegment + 1] - path[currentSegment]) * (timeOnSegment / totalSegmentTime);
|
|
else
|
|
{
|
|
if ( stopAtEnd && !path.empty()) newPos = path.back() + offset;
|
|
else if (reverseAtEnd)
|
|
{
|
|
std::reverse(path.begin(), path.end());
|
|
EnterSegment(0);
|
|
if (!path.empty()) newPos = path.front() + offset;
|
|
}
|
|
else
|
|
{
|
|
EnterSegment(0);
|
|
if (!path.empty()) newPos = path.front() + offset;
|
|
}
|
|
}
|
|
|
|
object->SetX(newPos.x);
|
|
object->SetY(newPos.y);
|
|
|
|
return;
|
|
}
|
|
|
|
void PathBehavior::DoStepPostEvents(RuntimeScene & scene)
|
|
{
|
|
if(!isPathLoaded)
|
|
{
|
|
LoadPath(scene);
|
|
Reset();
|
|
}
|
|
if(futureSegment != -1)
|
|
{
|
|
EnterSegment(futureSegment);
|
|
futureSegment = -1;
|
|
}
|
|
if(futurePosition != -1)
|
|
{
|
|
timeOnSegment = futurePosition * totalSegmentTime;
|
|
futurePosition = -1;
|
|
}
|
|
}
|
|
|
|
#if defined(GD_IDE_ONLY)
|
|
void PathBehavior::SerializeTo(gd::SerializerElement & element) const
|
|
{
|
|
SerializePathsTo(element.AddChild("paths"));
|
|
|
|
element.SetAttribute("currentPath", GetCurrentPathName());
|
|
element.SetAttribute("speed", GetSpeed());
|
|
element.SetAttribute("offsetX", GetOffsetX());
|
|
element.SetAttribute("offsetY", GetOffsetY());
|
|
element.SetAttribute("angleOffset", angleOffset);
|
|
element.SetAttribute("reverseAtEnd", ReverseAtEnd());
|
|
element.SetAttribute("stopAtEnd", StopAtEnd());
|
|
element.SetAttribute("followAngle", FollowAngle());
|
|
}
|
|
|
|
void PathBehavior::SerializePathsTo(gd::SerializerElement & element) const
|
|
{
|
|
element.ConsiderAsArrayOf("path");
|
|
for(std::map<gd::String, std::vector<sf::Vector2f> >::const_iterator it = localePaths.begin(); it != localePaths.end(); it++)
|
|
{
|
|
gd::SerializerElement & pathElement = element.AddChild("path");
|
|
|
|
pathElement.SetAttribute("name", it->first);
|
|
pathElement.SetAttribute("coords", GetStringFromCoordsVector(it->second, '/', ';'));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void PathBehavior::UnserializeFrom(const gd::SerializerElement & element)
|
|
{
|
|
UnserializePathsFrom(element.GetChild("paths", 0, "Paths"));
|
|
|
|
ChangeCurrentPath(element.GetStringAttribute("currentPath"));
|
|
speed = element.GetDoubleAttribute("speed");
|
|
offset.x = element.GetDoubleAttribute("offsetX");
|
|
offset.y = element.GetDoubleAttribute("offsetY");
|
|
angleOffset = element.GetDoubleAttribute("angleOffset");
|
|
reverseAtEnd = element.GetBoolAttribute("reverseAtEnd");
|
|
stopAtEnd = element.GetBoolAttribute("stopAtEnd");
|
|
followAngle = element.GetBoolAttribute("followAngle");
|
|
}
|
|
|
|
void PathBehavior::UnserializePathsFrom(const gd::SerializerElement & element)
|
|
{
|
|
localePaths.clear();
|
|
|
|
element.ConsiderAsArrayOf("path", "Path");
|
|
for(std::size_t i = 0;i<element.GetChildrenCount();++i)
|
|
{
|
|
const gd::SerializerElement & pathElement = element.GetChild(i);
|
|
localePaths[pathElement.GetStringAttribute("name")] = GetCoordsVectorFromString(pathElement.GetStringAttribute("coords"), '/', ';');
|
|
}
|
|
|
|
if(localePaths.empty())
|
|
localePaths["Object main path"] = std::vector<sf::Vector2f>(1, sf::Vector2f(0,0));
|
|
}
|
|
|
|
float PathBehavior::GetOffsetX() const
|
|
{
|
|
return offset.x;
|
|
}
|
|
|
|
void PathBehavior::SetOffsetX(float off)
|
|
{
|
|
offset.x = off;
|
|
}
|
|
|
|
float PathBehavior::GetOffsetY() const
|
|
{
|
|
return offset.y;
|
|
}
|
|
|
|
void PathBehavior::SetOffsetY(float off)
|
|
{
|
|
offset.y = off;
|
|
}
|
|
|
|
void PathBehavior::SetSpeed(float sp)
|
|
{
|
|
speed = sp;
|
|
}
|
|
|
|
float PathBehavior::GetSpeed() const
|
|
{
|
|
return speed;
|
|
}
|
|
|
|
void PathBehavior::SetAngleOffset(float off)
|
|
{
|
|
angleOffset = off;
|
|
|
|
//Update the angle
|
|
if(isPathLoaded)
|
|
{
|
|
sf::Vector2f newPath = (path[currentSegment + 1] - path[currentSegment]);
|
|
if(followAngle)
|
|
object->SetAngle(GetAngleOfSegment(newPath) + angleOffset);
|
|
}
|
|
}
|
|
|
|
float PathBehavior::GetAngleOffset() const
|
|
{
|
|
return angleOffset;
|
|
}
|
|
|
|
void PathBehavior::Reverse()
|
|
{
|
|
float tempTimeOnSegment = timeOnSegment;
|
|
std::reverse(path.begin(), path.end());
|
|
EnterSegment(path.size() - currentSegment - 2);
|
|
timeOnSegment = totalSegmentTime - tempTimeOnSegment;
|
|
}
|
|
|
|
const std::vector<sf::Vector2f>& PathBehavior::GetPath(const gd::String &_name) const
|
|
{
|
|
return localePaths.at(_name);
|
|
}
|
|
|
|
void PathBehavior::SetPath(const gd::String &_name, std::vector<sf::Vector2f> _path)
|
|
{
|
|
localePaths[_name] = _path;
|
|
}
|
|
|
|
void PathBehavior::ChangeCurrentPath(const gd::String &_name)
|
|
{
|
|
pathName = _name;
|
|
isPathLoaded = false;
|
|
|
|
Reset();
|
|
}
|
|
|
|
const gd::String& PathBehavior::GetCurrentPathName() const
|
|
{
|
|
return pathName;
|
|
}
|
|
|
|
void PathBehavior::LoadPath(RuntimeScene & scene)
|
|
{
|
|
if(localePaths.count(pathName) == 0)
|
|
{
|
|
if (runtimeScenesPathDatas == NULL)
|
|
runtimeScenesPathDatas = static_cast<RuntimeScenePathDatas*>(scene.GetBehaviorSharedData(name).get());
|
|
|
|
if(runtimeScenesPathDatas != NULL && runtimeScenesPathDatas->globalPaths.count(pathName) != 0)
|
|
path = std::vector<sf::Vector2f>(runtimeScenesPathDatas->globalPaths.at(pathName));
|
|
|
|
}
|
|
else
|
|
{
|
|
path = std::vector<sf::Vector2f>(GetPath(pathName));
|
|
}
|
|
|
|
isPathLoaded = true;
|
|
}
|
|
|
|
std::vector<gd::String> PathBehavior::GetListOfPathsNames() const
|
|
{
|
|
std::vector<gd::String> names;
|
|
for(std::map<gd::String, std::vector<sf::Vector2f> >::const_iterator it = localePaths.begin(); it != localePaths.end(); it++)
|
|
{
|
|
names.push_back((*it).first);
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
void PathBehavior::DeleteAllPaths()
|
|
{
|
|
localePaths.clear();
|
|
}
|
|
|
|
void PathBehavior::SetCurrentSegment(std::size_t seg)
|
|
{
|
|
futureSegment = seg;
|
|
}
|
|
|
|
int PathBehavior::GetCurrentSegment()
|
|
{
|
|
return futureSegment == -1 ? currentSegment : futureSegment;
|
|
}
|
|
|
|
float PathBehavior::GetPositionOnSegment()
|
|
{
|
|
return futurePosition == -1 ? (timeOnSegment / totalSegmentTime) : futurePosition;
|
|
}
|
|
|
|
void PathBehavior::SetPositionOnSegment(float pos)
|
|
{
|
|
//timeOnSegment = pos * totalSegmentTime;
|
|
futurePosition = pos;
|
|
}
|
|
|
|
gd::String PathBehavior::GetStringFromCoordsVector(const std::vector<sf::Vector2f> &vec, char32_t coordsSep, char32_t composantSep)
|
|
{
|
|
gd::String coordsStr;
|
|
|
|
for (std::size_t a = 0; a < vec.size(); a++)
|
|
{
|
|
coordsStr += gd::String::From(vec.at(a).x);
|
|
coordsStr.push_back(composantSep);
|
|
coordsStr += gd::String::From(vec.at(a).y);
|
|
if(a != vec.size() - 1)
|
|
coordsStr.push_back(coordsSep);
|
|
}
|
|
|
|
return coordsStr;
|
|
}
|
|
|
|
std::vector<sf::Vector2f> PathBehavior::GetCoordsVectorFromString(const gd::String &str, char32_t coordsSep, char32_t composantSep)
|
|
{
|
|
std::vector<sf::Vector2f> coordsVec;
|
|
|
|
std::vector<gd::String> coordsDecomposed = str.Split(coordsSep);
|
|
|
|
for(std::size_t a = 0; a < coordsDecomposed.size(); a++)
|
|
{
|
|
std::vector<gd::String> coordXY = coordsDecomposed.at(a).Split(composantSep);
|
|
|
|
if(coordXY.size() != 2)
|
|
continue;
|
|
|
|
sf::Vector2f newCoord(coordXY.at(0).To<float>(), coordXY.at(1).To<float>());
|
|
coordsVec.push_back(newCoord);
|
|
}
|
|
|
|
return coordsVec;
|
|
}
|
|
|
|
#define PI 3.14159265
|
|
|
|
float PathBehavior::GetAngleOfSegment(sf::Vector2f &seg)
|
|
{
|
|
float norm = std::abs(sqrtf(pow(seg.x, 2) + pow(seg.y,2)));
|
|
|
|
if(seg.y >= 0)
|
|
{
|
|
return acos(seg.x / norm) * 180.0 / PI;
|
|
}
|
|
else
|
|
{
|
|
return -acos(seg.x / norm) * 180.0 / PI;
|
|
}
|
|
}
|