I'm trying to write the Space Invaders clone for Windows console, and it felt like everything was going fine, but here comes this crash. The program compiles without errors, however instantly crashes on start up.
I admit that I don't know pointers very well, and I believe that the problem is somewhere there.
#include "stdafx.h"
#include <vector>
#include <random>
#include <chrono>
#include <thread>
#include <memory>
#include <SDKDDKVer.h>
#include "Vector2D.h"
#include "Renderer.h"
std::default_random_engine rGen;
typedef std::uniform_int_distribution<int> intRand;
typedef std::uniform_real_distribution<float> floatRand;
char ObjectType[][64] =
{
"ot_AlienShip",
"ot_PlayerShip",
"ot_AlienLaser",
"ot_PlayerLaser",
"ot_Explosion"
};
class PlayField;
class GameObject
{
public:
char* m_objType = nullptr;
Vector2D pos;
unsigned char sprite;
virtual void Update(PlayField& world) {};
virtual bool DecreaseHealth() { return true; };
};
class Input
{
public:
virtual bool Left() = 0;
virtual bool Right() = 0;
virtual bool Fire() = 0;
};
class RndInput : public Input
{
public:
virtual bool Left() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.3f); }
virtual bool Right() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.4f); };
virtual bool Fire() override { floatRand keyRate(0, 1); return (keyRate(rGen) < 0.5f); };
};
class PlayField
{
private:
typedef GameObject* GameObjPtr;
std::vector<GameObjPtr> gameObjects;
public:
Input* cotrollerInput = nullptr;
GameObject* it = new GameObject;
//it = new GameObject;
Vector2D bounds;
// Number of available active laser slots for aliens and player
int AlienLasers = 10;
int PlayerLasers = 4;
explicit PlayField(Vector2D iBounds) : bounds(iBounds) {};
const std::vector<GameObjPtr>& GameObjects() { return gameObjects; }
void Update()
{
// update list of active objects in the world
for (auto it : gameObjects)
{
it->Update(*this); //The crash is here "Unhandled exception thrown: read access violation. it was 0xFFFFFFFFFFFFFFFF."
}
}
GameObject* GetPlayerObject()
{
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [](GameObjPtr in) { return (strcmp(in->m_objType,"ot_PlayerShip")==0); });
if (it != gameObjects.end())
return (*it);
else
return nullptr;
}
void SpawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers--;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers--;
AddObject(newObj);
}
void DespawnLaser(GameObject* newObj)
{
if (strcmp(newObj->m_objType, "ot_AlienLaser")==0)
AlienLasers++;
else if (strcmp(newObj->m_objType, "ot_PlayerLaser")==0)
PlayerLasers++;
RemoveObject(newObj);
}
void AddObject(GameObject* newObj)
{
//gameObjectsToAdd.push_back(GameObjPtr(newObj));
gameObjects.push_back(newObj);
}
void RemoveObject(GameObject* newObj)
{
//gameObjectsToRemove.push_back(newObj);
auto it = std::find_if(gameObjects.begin(), gameObjects.end(), [&](GameObjPtr in) { return (in==newObj); });
gameObjects.erase(it);
}
};
class Explosion : public GameObject
{
public:
// Explosion lasts 5 ticks before it dissappears
int timer = 5;
Explosion() { m_objType = new char[64]; strcpy(m_objType, "ot_Explosion"); sprite = RS_Explosion; }
~Explosion() { delete[] m_objType; }
void Update(PlayField& world) override
{
timer--;
if (!timer)
{
world.RemoveObject(this);
delete this;
}
}
};
class AlienLaser : public GameObject
{
public:
AlienLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienLaser"); sprite = RS_AlienLaser; }
~AlienLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y += 1.f;
if (pos.y > world.bounds.y)
{
deleted = true;
}
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
deleted = true;
//Spawn explosion, kill player
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
if (deleted)
{
world.DespawnLaser((GameObject*)this);
delete this;
}
}
};
class PlayerLaser : public GameObject
{
public:
PlayerLaser() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerLaser"); sprite = RS_PlayerLaser; }
~PlayerLaser() { delete[] m_objType; }
void Update(PlayField& world) override
{
bool deleted = false;
pos.y -= 1.f;
if (pos.y < 0)
{
deleted = true;
}
for (auto it : world.GameObjects())
{
if (strcmp(it->m_objType,"ot_AlienShip")==0 && it->pos.IntCmp(pos))
{
deleted = true;
//Spawn explosion, kill the alien that we hit
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
if (it->DecreaseHealth())
world.RemoveObject(it);
}
}
if (deleted)
{
world.DespawnLaser(this);
delete this;
}
}
};
class Alien : public GameObject
{
public:
Alien() { m_objType = new char[64]; strcpy(m_objType, "ot_AlienShip"); sprite = RS_Alien; }
~Alien() { delete m_objType; }
private:
const float maxUpdateRate = 0.01f;
const float transformEnergy = 1.f;
enum AlienState
{
as_Normal,
as_Better
};
// Variables dictating energy level for upgrade, direction of movement, and current speed
float health = 1.f;
float energy = 0.f;
float direction = 1.f;
float velocity = 0.5f;
AlienState state{};
void Transform()
{
state = as_Better;
sprite = RS_BetterAlien;
velocity *= 2.f;
}
bool DecreaseHealth() override { health -= 1.f; return health <= 0; }
void Update(PlayField& world) override
{
pos.x += direction * velocity;
// Border check
if (pos.x < 0 || pos.x >= world.bounds.x - 1)
{
direction = -direction;
pos.y += 1;
}
// Border check vertical:
if (pos.y >= world.bounds.y - 1)
{
// kill player
GameObject* player = world.GetPlayerObject();
if (player && pos.IntCmp(player->pos))
{
//Spawn explosion
GameObject& no = *(new Explosion);
no.pos = pos;
world.AddObject(&no);
world.RemoveObject(player);
}
}
// Transform into better Alien
if (state!=as_Better)
{
floatRand updateRate(-maxUpdateRate, 2*maxUpdateRate);
energy += updateRate(rGen);
if (energy >= transformEnergy)
Transform();
}
floatRand fireRate(0, 1);
if (fireRate(rGen) < 0.5 && world.AlienLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new AlienLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
class PlayerShip : public GameObject
{
public:
PlayerShip() { m_objType = new char[64]; strcpy(m_objType, "ot_PlayerShip"); sprite = RS_Player; }
~PlayerShip() { delete m_objType; }
void Update(PlayField& world) override
{
if (world.cotrollerInput->Left())
pos.x -= 1;
else if (world.cotrollerInput->Right())
pos.x += 1;
if (world.cotrollerInput->Fire() && world.PlayerLasers>0)
{
//Spawn laser
GameObject& newLaser = *(new PlayerLaser);
newLaser.pos = pos;
world.SpawnLaser(&newLaser);
}
}
};
int main()
{
rGen.seed(1);
Vector2D size(80, 28);
Renderer mainRenderer(size);
PlayField world(size);
intRand xCoord(0, (int)size.x-1);
intRand yCoord(0, 10);
// Populate aliens
for (int k = 0; k < 20; k++)
{
Alien& a = *(new Alien);
a.pos.x = (float)xCoord(rGen);
a.pos.y = (float)yCoord(rGen);
world.AddObject(&a);
}
// Add player
PlayerShip& p = *(new PlayerShip);
p.pos = Vector2D(40, 27);
world.AddObject(&p);
world.Update();
{
RenderItemList rl;
for (auto it : world.GameObjects())
{
RenderItem a = RenderItem(Vector2D(it->pos), it->sprite);
rl.push_back(a);
}
mainRenderer.Update(rl);
// Sleep a bit so updates don't run too fast
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return 0;
}
In addition, VS gives the following info:
I assume, that the pointer (or the object) appears to be cleared somewhere before, but I have no idea how to trace it. Thanks in advance.
The various ::Update methods you have add and remove objects from gameObjects while you are traversing it in Playfield::Update. This is a guaranteed crash as it invalidates the implicit iterator in your for loop.
To solve this, either:
Have GameObject::Update return a boolean that signifies if the object can be removed or not. This requires you to rewrite the loop in Playfield::Update with explicit iterators so you an do it = gameObjects.erase(it);. This will still not allow you to add new objects, though.
Defer additions and removals until the end of the game loop. Add a markForAddition / markForRemoval method that will remove these objects from the game world after Playfield::Update has gone through. You will need to do some extra bookkeeping to make sure you do not update or draw objects that have been removed earlier in the same loop, but that is surmountable.
Switch data structures: a std::list does not invalidate its iterators if you remove an object somewhere. You do still need to be careful with the current element though.
Related
So I have two classes "PlayerTracking" and "Projectile". Every instance of player tracking should have an associated projectile; the PlayerTracking class has a pointer to a projectile to connect the two. When I call the "getPlayerProjectile" function I would like the constructor in the Projectile class to initialise the projectile but I don't really have any idea what is the best way to go about doing this. Here is the player tracking.h file
#pragma once
#include"GameObject.h"
#include"Projectile.h"
class playerTracking :public GameObject {
public:
playerTracking() {
playerYawOrientation = 0.0f;
playerPitchOrientation = 0.0f;
playerID = 1;
teamID = 0;
IndividualplayerScore = 0;
playerProjectile = new Projectile();
}
void setplayerID(int assignedPlayerID) {
playerID = assignedPlayerID;
}
void setTeamID(int assignedTeamID) {
playerID = assignedTeamID;
}
void AssignPlayerWeapon(gun weponType) {
playerProjectile->setGunType(weponType);
}
Projectile* getPlayerProjectile() {
return playerProjectile;
}
protected:
float playerYawOrientation;
float playerPitchOrientation;
int playerID;
int teamID;
int IndividualplayerScore;
Projectile* playerProjectile;
};
and here is the projectile class.h
#pragma once
#include "Transform.h"
#include "CollisionVolume.h"
#include"GameObject.h"
struct gun {
float radius;
float projectileForce;
float weight;
bool affectedByGravity;
};
static gun pistol{
7,
50,
1,
false,
};
static gun rocket{
20,
20,
5,
true,
};
class NetworkObject;
class RenderObject;
class PhysicsObject;
class Projectile:public GameObject {
public:
/* Projectile() {
setGunType(pistol);
aimingPitch = 0.0f;
aimingYaw = 0.0f;
physicsProjectile = nullptr;
}*/
Projectile();
~Projectile() {
physicsProjectile;
}
void setGunType(gun wepType);
void setExplosionRadius(float newRadius) {
explosionRadius = newRadius;
}
float getExplosionRadius() {
return explosionRadius;
}
void setProjectilePropultionForce(float newForce) {
projectilePropultionForce = newForce;
}
float getPojectilePropultionForce() {
return projectilePropultionForce;
}
void setWieght(float newweight) {
weight = newweight;
}
float getWeight() {
return weight;
}
void setAffectedByGravityTrue() {
AffectedGravity = true;
}
void setAffectedByGravityFalse() {
AffectedGravity = false;
}
void setBulletDirectionVector(Vector3 aimedDirection) {
bulletDirectionVector = aimedDirection.Normalised();
}
Vector3 getBulletDirectionVector() {
return bulletDirectionVector;
}
bool getAffectedGravity() {
return AffectedGravity;
}
protected:
float explosionRadius;
float projectilePropultionForce;
float weight;
float aimingYaw;
float aimingPitch;
//static int TeamID;
//static int personalID;
bool AffectedGravity;
PhysicsObject* physicsProjectile;
Vector3 bulletDirectionVector;
};
and the Projectile.c file
#include"Projectile.h"
Projectile::Projectile(){
setGunType(pistol);
aimingPitch = 0.0f;
aimingYaw = 0.0f;
physicsProjectile = nullptr;
bulletDirectionVector = { 0,0,0 };
}
void Projectile::setGunType(gun wepType) {
setExplosionRadius(wepType.radius);
setProjectilePropultionForce(wepType.projectileForce);
setWieght(wepType.weight);
//SetBoundingVolume(VolumeType::Sphere);
if (wepType.affectedByGravity) {
setAffectedByGravityTrue();
}
else
{
setAffectedByGravityFalse();
}
}
Obviously right now the pointer playerProjectile is just null but I am pretty sure using new would be a bad way to construct one in this case. Thanks a bunch for any help!
i'm trying to render an obj file by loading it through a custom component.
The model on the left is straigth out exported from blender, using a scale of 100, while the model on the right is from the same .obj file, but parsed drectly from code and rendered by building a custom Vertex Factory, Index Buffer and Position Buffer, with FMeshBatch::bWireframe = true :
As you can see, the model on the right renders with noticeable visual artifacts.
I started by creating a custom class that extends UPrimitiveComponent and a custom proxy for it that extends FPrimitiveComponent:
// UTestPrimitiveComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/PrimitiveComponent.h"
#include "UTestPrimitiveComponent.generated.h"
class FPrimitiveSceneProxy;
class FObjectInitializer;
class UStaticMesh;
class FStaticMeshDescription;
struct FRawMesh;
USTRUCT()
struct FOBJMesh {
GENERATED_BODY()
TArray<FVector> vertices;
TArray<FVector> normals;
TArray<FVector2D> uvs;
TArray<int32> tex_indices; // Unused
TArray<uint32> vertex_indices;
TArray<int32> normals_indices; // Unused
};
UCLASS(ClassGroup = (Custom), meta = (BlueprintSpawnableComponent))
class RENDERINGPIPELINE_API UTestPrimitiveComponent :
public UPrimitiveComponent
{
GENERATED_BODY()
private:
FOBJMesh* TheMesh;
void ParseLine(FString& line, FRawMesh& Desc);
void ReadNormalFromLine(FString& line, FVector& normal);
void ReadUVFromLine(FString& line, FVector2D& uv);
void ReadVertexFromLine(FString& line, FVector& vertex);
void ReadFaceInfoFromLine(FString& element, FRawMesh& Desc);
public:
UTestPrimitiveComponent(const FObjectInitializer&);
void BeginPlay() override;
__forceinline FOBJMesh* GetMesh() { return TheMesh; }
FPrimitiveSceneProxy* CreateSceneProxy() override;
};
While this is the listing for both the proxy and the component:
#include "UTestPrimitiveComponent.h"
#include "Components/MeshComponent.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "Engine/StaticMesh.h"
#include "PrimitiveSceneProxy.h"
#include "StaticMeshDescription.h"
#include "Rendering/StaticMeshVertexBuffer.h"
#include "RawMesh.h"
#include "RHICommandList.h"
#include "PrimitiveSceneProxy.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Misc/DefaultValueHelper.h"
#include "Containers/Array.h"
#include "EngineMinimal.h"
class UTestPrimitiveComponent;
template <typename T>
static FVertexBufferRHIRef CreateVertexBufferWithData(TArray<T>& data) {
FRHIResourceCreateInfo info;
auto size = sizeof (T)* data.Num();
FVertexBufferRHIRef buf = RHICreateVertexBuffer(size, BUF_Static | BUF_ShaderResource, info);
void* ptr = static_cast<void*>(RHILockVertexBuffer(buf, 0, size, RLM_WriteOnly));
FMemory::Memcpy(ptr, static_cast<void*>(data.GetData()), size);
RHIUnlockVertexBuffer(buf);
return buf;
}
struct FOBJIndexBuffer : public FIndexBuffer {
TArray<uint32> Indices;
virtual void InitRHI() override {
FRHIResourceCreateInfo info;
auto ElementSizeInBytes = sizeof(uint32);
auto ArraySize = ElementSizeInBytes * Indices.Num();
IndexBufferRHI = RHICreateIndexBuffer(ElementSizeInBytes, ArraySize, BUF_Static, info);
void* ptr = static_cast<void*>(RHILockIndexBuffer(IndexBufferRHI, 0, ArraySize, RLM_WriteOnly));
FMemory::Memcpy(ptr, static_cast<void*>(Indices.GetData()), ArraySize);
RHIUnlockIndexBuffer(IndexBufferRHI);
}
};
struct FOBJPositionBuffer : public FVertexBuffer {
TArray<FVector> Vertices;
FShaderResourceViewRHIRef PositionComponentSRV;
virtual void InitRHI() override {
FRHIResourceCreateInfo info;
VertexBufferRHI = CreateVertexBufferWithData(Vertices);
if (VertexBufferRHI)
{
PositionComponentSRV =
RHICreateShaderResourceView(FShaderResourceViewInitializer(VertexBufferRHI, PF_R32_FLOAT));
}
}
};
struct FOBJMeshBuffers : public FRenderResource {
struct FTangentData {
FPackedNormal X;
FPackedNormal Z;
public:
FTangentData(FPackedNormal InZ, FPackedNormal InX)
: X(InX), Z(InZ) {}
};
FVertexBuffer TexCoordsBuffer;
FVertexBuffer TangentsBuffer;
TArray<FVector2D> uvs;
TArray<FTangentData> Tangents;
FShaderResourceViewRHIRef TexCoordsBufferSRV;
FShaderResourceViewRHIRef TangentsBufferSRV;
virtual void InitRHI() override {
TexCoordsBuffer.VertexBufferRHI = CreateVertexBufferWithData(uvs);
if (TexCoordsBuffer.VertexBufferRHI) {
// PF_G16R16F if using half precision, todo when implementing full thing
TexCoordsBufferSRV = RHICreateShaderResourceView(FShaderResourceViewInitializer(TexCoordsBuffer.VertexBufferRHI, PF_G32R32F));
}
TangentsBuffer.VertexBufferRHI = CreateVertexBufferWithData(Tangents);
if (TangentsBuffer.VertexBufferRHI) {
// PF_R8G8B8A8_SNORM if using half precision, todo when implementing full thing
TangentsBufferSRV =
RHICreateShaderResourceView(FShaderResourceViewInitializer(TangentsBuffer.VertexBufferRHI, PF_R8G8B8A8_SNORM));
}
BeginInitResource(&TexCoordsBuffer);
BeginInitResource(&TangentsBuffer);
}
};
struct FOBJRenderData {
FOBJPositionBuffer OBJPositionBuffer;
FOBJMeshBuffers OBJMeshBuffers;
};
class OBJSceneProxy : public FPrimitiveSceneProxy {
public:
UMaterialInterface* Material;
FLocalVertexFactory VertexFactory;
FOBJMeshBuffers MeshBuffers;
FOBJPositionBuffer PositionBuffer;
FOBJIndexBuffer IndexBuffer;
UTestPrimitiveComponent* TheComponent;
OBJSceneProxy(UTestPrimitiveComponent* Component)
: FPrimitiveSceneProxy(Component, TEXT("OBJ Component")),
VertexFactory(GetScene().GetFeatureLevel(), "FObjSceneProxy"),
TheComponent(Component) {
IndexBuffer.Indices = TheComponent->GetMesh()->vertex_indices;
PositionBuffer.Vertices = TheComponent->GetMesh()->vertices;
MeshBuffers.uvs = Component->GetMesh()->uvs;
for (int i = 1; i < IndexBuffer.Indices.Num(); i++) {
// This block of code here is mostly wrong; Still, it shouldn't impact the rendering as much
// Blender y u start counting from 1
auto CurIndex = IndexBuffer.Indices[i] - 1;
auto PrevIndex = IndexBuffer.Indices[i - 1] - 1;
auto prevVertex = PositionBuffer.Vertices[PrevIndex];
auto curVertex = PositionBuffer.Vertices[CurIndex];
auto TangentRight = (curVertex - prevVertex).GetSafeNormal();
auto TangentFront = FVector::CrossProduct(TangentRight, curVertex.GetSafeNormal());
MeshBuffers.Tangents.Add(FOBJMeshBuffers::FTangentData(TangentFront, TangentRight));
}
BeginInitResource(&IndexBuffer);
BeginInitResource(&PositionBuffer);
BeginInitResource(&MeshBuffers);
InitFactory();
BeginInitResource(&VertexFactory);
Material = UMaterial::GetDefaultMaterial(MD_Surface);
}
virtual ~OBJSceneProxy()
{
IndexBuffer.ReleaseResource();
PositionBuffer.ReleaseResource();
MeshBuffers.TexCoordsBuffer.ReleaseResource();
MeshBuffers.TangentsBuffer.ReleaseResource();
MeshBuffers.ReleaseResource();
VertexFactory.ReleaseResource();
}
void InitFactory() {
struct FactoryParams {
FLocalVertexFactory* Factory;
FOBJMeshBuffers* MeshBuffers;
FOBJPositionBuffer* PositionBuffer;
} Params;
Params.Factory = &VertexFactory;
Params.PositionBuffer = &PositionBuffer;
Params.MeshBuffers = &MeshBuffers;
ENQUEUE_RENDER_COMMAND(OBJInitVertexFactory)([Params] (FRHICommandListImmediate& RHICmdList) {
FLocalVertexFactory::FDataType Data;
// Position stuff
Data.PositionComponent = FVertexStreamComponent(
Params.PositionBuffer,
0,
sizeof(FVector),
VET_Float3
);
Data.PositionComponentSRV = Params.PositionBuffer->PositionComponentSRV;
// Tex Coords stuff
Data.TextureCoordinatesSRV = Params.MeshBuffers->TexCoordsBufferSRV;
Data.TextureCoordinates.Add(FVertexStreamComponent(
&Params.MeshBuffers->TexCoordsBuffer,
0,
sizeof(FVector2D),
VET_Float4, // Doppio della dimensione (VET_Float2): Why?
EVertexStreamUsage::ManualFetch
));
// Tangents stuff
Data.TangentsSRV = Params.MeshBuffers->TangentsBufferSRV;
typedef FPackedNormal TangentType;
auto TangentElemType = VET_Short2;
auto TangentXOffset = STRUCT_OFFSET(FOBJMeshBuffers::FTangentData, X);
auto TangentZOffset = STRUCT_OFFSET(FOBJMeshBuffers::FTangentData, Z);
auto TangentSizeInBytes = sizeof(TangentType);
Data.TangentBasisComponents[0] = FVertexStreamComponent(
&Params.MeshBuffers->TangentsBuffer,
TangentXOffset,
TangentSizeInBytes,
TangentElemType,
EVertexStreamUsage::ManualFetch
);
Data.TangentBasisComponents[1] = FVertexStreamComponent(
&Params.MeshBuffers->TangentsBuffer,
TangentZOffset,
TangentSizeInBytes,
TangentElemType,
EVertexStreamUsage::ManualFetch
);
Data.LightMapCoordinateComponent = FVertexStreamComponent(
&Params.MeshBuffers->TexCoordsBuffer,
0,
sizeof(FVector2D),
VET_Float2,
EVertexStreamUsage::ManualFetch
);
FColorVertexBuffer::BindDefaultColorVertexBuffer(Params.Factory, Data,
FColorVertexBuffer::NullBindStride::FColorSizeForComponentOverride);
Params.Factory->SetData(Data);
Params.Factory->InitResource();
});
}
// (StartIndex + IndexCount) * IndexBuffer->GetStride()
void DrawStaticElements(FStaticPrimitiveDrawInterface* DrawInterface)
{
check(IsInRenderingThread());
FMaterialRenderProxy* Proxy = Material->GetRenderProxy();
FMeshBatch batch;
batch.bWireframe = true;
batch.VertexFactory = &VertexFactory;
batch.Type = PT_TriangleList;
batch.MaterialRenderProxy = Proxy;
batch.DepthPriorityGroup = SDPG_World;
batch.CastShadow = true;
batch.LODIndex = 0;
auto& BatchElement = batch.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
// for i = FirstIndex to NumPrimitives ?
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
DrawInterface->DrawMesh(batch, MAX_FLT);
}
FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) const
{
// Partially copied from StaticMeshRenderer.cpp
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = true;
Result.bRenderInMainPass = true;
Result.bVelocityRelevance = IsMovable() && Result.bOpaque && Result.bRenderInMainPass;
Result.bStaticRelevance = true;
Result.bDynamicRelevance = false;
return Result;
}
};
UTestPrimitiveComponent::UTestPrimitiveComponent(const FObjectInitializer& ObjectInitializer) :
UPrimitiveComponent(ObjectInitializer) {
UActorComponent::PrimaryComponentTick.bCanEverTick = true;
TheMesh = new FOBJMesh();
FString result;
auto gamePath = FPaths::ProjectContentDir();
auto fullPath = FPaths::Combine(gamePath, TEXT("RawContent"), TEXT("cube.obj"));
FFileHelper::LoadFileToString(result, *fullPath);
FRawMesh meshInfo;
TArray<FString> lines;
result.ParseIntoArrayLines(lines);
for (auto line : lines) {
ParseLine(line, meshInfo);
}
}
void UTestPrimitiveComponent::BeginPlay()
{
GEngine->AddOnScreenDebugMessage(1, 5.0f, FColor::Red,
FString::Printf(TEXT("Vertices: %d, UVs: %d, Normals: %d"), TheMesh->vertices.Num(), TheMesh->uvs.Num(), TheMesh->normals.Num()));
GEngine->AddOnScreenDebugMessage(2, 5.0f, FColor::Red,
FString::Printf(TEXT("VIndices: %d, UVIndices: %d, NIndices: %d"), TheMesh->vertex_indices.Num(), TheMesh->tex_indices.Num(), TheMesh->normals_indices.Num()));
}
FPrimitiveSceneProxy* UTestPrimitiveComponent::CreateSceneProxy()
{
return (FPrimitiveSceneProxy*)(new OBJSceneProxy(this));
}
void UTestPrimitiveComponent::ParseLine(FString& line, FRawMesh& Desc)
{
if (line[0] == 'v') {
if (line[1] == 'n') {
// It is a normal
line.RemoveFromStart(TEXT("vn "));
FVector normal;
ReadNormalFromLine(line, normal);
TheMesh->normals.Add(normal);
}
else if (line[1] == 't') {
// It is an uv vertex
line.RemoveFromStart(TEXT("vt "));
FVector2D uv;
ReadUVFromLine(line, uv);
TheMesh->uvs.Add(uv);
}
else {
// It is a vertex
line.RemoveFromStart(TEXT("v "));
FVector vertex;
ReadVertexFromLine(line, vertex);
TheMesh->vertices.Add(vertex);
}
}
else if (line[0] == 'f') {
line.RemoveFromStart("f ");
TArray<FString> elements;
line.ParseIntoArray(elements, TEXT(" "));
for (int i = 0; i < elements.Num(); i++) {
ReadFaceInfoFromLine(elements[i], Desc);
}
}
// Ignore the line if it's a comment, a usrlib/matlib/other stuff directive
else return;
}
void UTestPrimitiveComponent::ReadNormalFromLine(FString& line, FVector& normal)
{
TArray<FString> elements;
line.ParseIntoArray(elements, TEXT(" "));
FDefaultValueHelper::ParseFloat(elements[0], normal.X);
FDefaultValueHelper::ParseFloat(elements[1], normal.Y);
FDefaultValueHelper::ParseFloat(elements[2], normal.Z);
}
void UTestPrimitiveComponent::ReadUVFromLine(FString& line, FVector2D& uv)
{
TArray<FString> elements;
line.ParseIntoArray(elements, TEXT(" "));
FDefaultValueHelper::ParseFloat(elements[0], uv.X);
FDefaultValueHelper::ParseFloat(elements[1], uv.Y);
if (elements.Num() == 3) {
// We have a w! Ignore, UE4 does not support it
// FDefaultValueHelper::ParseFloat(elements[2], uv.Z);
}
}
void UTestPrimitiveComponent::ReadVertexFromLine(FString& line, FVector& vertex)
{
TArray<FString> elements;
line.ParseIntoArray(elements, TEXT(" "));
FDefaultValueHelper::ParseFloat(elements[0], vertex.X);
FDefaultValueHelper::ParseFloat(elements[1], vertex.Y);
FDefaultValueHelper::ParseFloat(elements[2], vertex.Z);
if (elements.Num() == 4) {
// We have a w! UE4 does not support it
// FDefaultValueHelper::ParseFloat(elements[3], vertex.W);
}
}
void UTestPrimitiveComponent::ReadFaceInfoFromLine(FString& element, FRawMesh& Desc)
{
int vertex_index;
// Splitting the vi/ui/ni triplet
TArray<FString> tripletString;
element.ParseIntoArray(tripletString, TEXT("/"));
FDefaultValueHelper::ParseInt(tripletString[0], vertex_index);
TheMesh->vertex_indices.Add(vertex_index);
/* UE4 does not use this stuff
if (tripletString.Num() > 1) {
FDefaultValueHelper::ParseInt(tripletString[1], uv_index);
TheMesh->tex_indices.Add(uv_index);
}
if (tripletString.Num() > 2) {
FDefaultValueHelper::ParseInt(tripletString[2], normal_index);
TheMesh->normals_indices.Add(normal_index);
}
*/
}
I have absolutely no idea why the rendering is broken, could anyone help me?
Solved, the issue was that Blender starts counting vertices from 1 (so yes, the problem was how i was reading the obj file)
I have some code that when compiled, runs an Asteroid Game. I want to make some changes. In place of sprites for the ship, I would like to use trianges. For a bullet, I'd like to use a small rectangle, and finally, a polygon for the asteroids. The code uses an Entity Master Class with a list. Can somebody please elaborate on how to make these changes?
#include <SFML/Graphics.hpp>
#include <time.h>
#include <list>
using namespace sf;
const int W = 1200;
const int H = 800;
float DEGTORAD = 0.017453f;
class Animation
{
public:
float Frame, speed;
Sprite sprite;
std::vector<IntRect> frames;
Animation(){}
Animation (Texture &t, int x, int y, int w, int h, int count, float Speed)
{
Frame = 0;
speed = Speed;
for (int i=0;i<count;i++)
frames.push_back( IntRect(x+i*w, y, w, h) );
sprite.setTexture(t);
sprite.setOrigin(w/2,h/2);
sprite.setTextureRect(frames[0]);
}
void update()
{
Frame += speed;
int n = frames.size();
if (Frame >= n) Frame -= n;
if (n>0) sprite.setTextureRect( frames[int(Frame)] );
}
bool isEnd()
{
return Frame+speed>=frames.size();
}
};
class Entity
{
public:
float x,y,dx,dy,R,angle;
bool life;
std::string name;
Animation anim;
Entity()
{
life=1;
}
void settings(Animation &a,int X,int Y,float Angle=0,int radius=1)
{
anim = a;
x=X; y=Y;
angle = Angle;
R = radius;
}
virtual void update(){};
void draw(RenderWindow &app)
{
anim.sprite.setPosition(x,y);
anim.sprite.setRotation(angle+90);
app.draw(anim.sprite);
CircleShape circle(R);
circle.setFillColor(Color(255,0,0,170));
circle.setPosition(x,y);
circle.setOrigin(R,R);
//app.draw(circle);
}
virtual ~Entity(){};
};
class asteroid: public Entity
{
public:
asteroid()
{
dx=rand()%8-4;
dy=rand()%8-4;
name="asteroid";
}
void update()
{
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
class bullet: public Entity
{
public:
bullet()
{
name="bullet";
}
void update()
{
dx=cos(angle*DEGTORAD)*6;
dy=sin(angle*DEGTORAD)*6;
// angle+=rand()%7-3; /*try this*/
x+=dx;
y+=dy;
if (x>W || x<0 || y>H || y<0) life=0;
}
};
class player: public Entity
{
public:
bool thrust;
player()
{
name="player";
}
void update()
{
if (thrust)
{ dx+=cos(angle*DEGTORAD)*0.2;
dy+=sin(angle*DEGTORAD)*0.2; }
else
{ dx*=0.99;
dy*=0.99; }
int maxSpeed=15;
float speed = sqrt(dx*dx+dy*dy);
if (speed>maxSpeed)
{ dx *= maxSpeed/speed;
dy *= maxSpeed/speed; }
x+=dx;
y+=dy;
if (x>W) x=0; if (x<0) x=W;
if (y>H) y=0; if (y<0) y=H;
}
};
bool isCollide(Entity *a,Entity *b)
{
return (b->x - a->x)*(b->x - a->x)+
(b->y - a->y)*(b->y - a->y)<
(a->R + b->R)*(a->R + b->R);
}
int main()
{
srand(time(0));
RenderWindow app(VideoMode(W, H), "Asteroids!");
app.setFramerateLimit(60);
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
t1.setSmooth(true);
t2.setSmooth(true);
Sprite background(t2);
Animation sExplosion(t3, 0,0,256,256, 48, 0.5);
Animation sRock(t4, 0,0,64,64, 16, 0.2);
Animation sRock_small(t6, 0,0,64,64, 16, 0.2);
Animation sBullet(t5, 0,0,32,64, 16, 0.8);
Animation sPlayer(t1, 40,0,40,40, 1, 0);
Animation sPlayer_go(t1, 40,40,40,40, 1, 0);
Animation sExplosion_ship(t7, 0,0,192,192, 64, 0.5);
std::list<Entity*> entities;
for(int i=0;i<15;i++)
{
asteroid *a = new asteroid();
a->settings(sRock, rand()%W, rand()%H, rand()%360, 25);
entities.push_back(a);
}
player *p = new player();
p->settings(sPlayer,200,200,0,20);
entities.push_back(p);
/////main loop/////
while (app.isOpen())
{
Event event;
while (app.pollEvent(event))
{
if (event.type == Event::Closed)
app.close();
if (event.type == Event::KeyPressed)
if (event.key.code == Keyboard::Space)
{
bullet *b = new bullet();
b->settings(sBullet,p->x,p->y,p->angle,10);
entities.push_back(b);
}
}
if (Keyboard::isKeyPressed(Keyboard::Right)) p->angle+=3;
if (Keyboard::isKeyPressed(Keyboard::Left)) p->angle-=3;
if (Keyboard::isKeyPressed(Keyboard::Up)) p->thrust=true;
else p->thrust=false;
for(auto a:entities)
for(auto b:entities)
{
if (a->name=="asteroid" && b->name=="bullet")
if ( isCollide(a,b) )
{
a->life=false;
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion,a->x,a->y);
e->name="explosion";
entities.push_back(e);
for(int i=0;i<2;i++)
{
if (a->R==15) continue;
Entity *e = new asteroid();
e->settings(sRock_small,a->x,a->y,rand()%360,15);
entities.push_back(e);
}
}
if (a->name=="player" && b->name=="asteroid")
if ( isCollide(a,b) )
{
b->life=false;
Entity *e = new Entity();
e->settings(sExplosion_ship,a->x,a->y);
e->name="explosion";
entities.push_back(e);
p->settings(sPlayer,W/2,H/2,0,20);
p->dx=0; p->dy=0;
}
}
if (p->thrust) p->anim = sPlayer_go;
else p->anim = sPlayer;
for(auto e:entities)
if (e->name=="explosion")
if (e->anim.isEnd()) e->life=0;
if (rand()%150==0)
{
asteroid *a = new asteroid();
a->settings(sRock, 0,rand()%H, rand()%360, 25);
entities.push_back(a);
}
for(auto i=entities.begin();i!=entities.end();)
{
Entity *e = *i;
e->update();
e->anim.update();
if (e->life==false) {i=entities.erase(i); delete e;}
else i++;
}
//////draw//////
app.draw(background);
for(auto i:entities) i->draw(app);
app.display();
}
return 0;
}
it is not really relevant question to be asked here,
but take a look at this part of code:
Texture t1,t2,t3,t4,t5,t6,t7;
t1.loadFromFile("images/spaceship.png");
t2.loadFromFile("images/background.jpg");
t3.loadFromFile("images/explosions/type_C.png");
t4.loadFromFile("images/rock.png");
t5.loadFromFile("images/fire_blue.png");
t6.loadFromFile("images/rock_small.png");
t7.loadFromFile("images/explosions/type_B.png");
I have a problem that arised with another question I made. I am using a list of pointers of a custom class (class that I have implemented), and the program crashes when I call this method, however, if I do not use a pointer list, it works fine. This is the function where the program crashes:
void enemy_spawner()
{
Point2D enemy_pos = Point2D(rand() % (SCREEN_WIDTH - 20) + 20, -20);
Point2D enemy_vel = Point2D(0, rand() % 4 + 1);
Sprite* enemy = new Sprite(enemy_pos, enemy_vel, enemy_image, enemy_info);
//Kamikaze* enemy = new Kamikaze(enemy_pos, enemy_vel, 0, enemy_image, enemy_info);
if (enemy_group.size() < MAX_ENEMIES)
{
// Program crashes here
enemy_group.push_back(enemy);
}
}
The enemy_group is a global variable defined as follows:
std::list<Sprite*> enemy_group;
My Sprite class is as follows (I have read that I need to have properly defined copy constructors, etc)
class Sprite
{
private:
Point2D sp_pos;
Point2D sp_vel;
SDL_Surface* sp_img;
Point2D sp_center;
Point2D sp_size;
double sp_radius;
bool sp_animated;
int sp_frames;
int sp_cur_frame;
public:
Sprite() {}
Sprite(Point2D pos, Point2D vel, SDL_Surface *img, ImageInfo info, bool animated = false, int frames = 0);
virtual void draw(SDL_Surface* screen);
virtual void update();
void setInfo (ImageInfo info);
void setPos( Point2D pos ) { sp_pos = pos; }
void setVel( Point2D vel ) { sp_vel = vel; }
void setImg (SDL_Surface* img) { sp_img = img; }
void setNextFrame() { sp_cur_frame++; }
void setFrame( int frame ) { sp_cur_frame = frame; }
void setAnimated(bool animated) { sp_animated = animated; }
void changeVelX (int c) { sp_vel.setX(c);}
void changeVelY (int c) { sp_vel.setY(c);}
void changePosX (int c) { sp_pos.setX(c);}
void changePosY (int c) { sp_pos.setY(c);}
SDL_Surface* getImg() { return sp_img; }
Point2D getPos() { return sp_pos; }
Point2D getVel() { return sp_vel; }
Point2D getCenter() { return sp_center; }
Point2D getSize() { return sp_size; }
double getRadius() { return sp_radius; }
int getCurFrame() { return sp_cur_frame; }
int getFrames() { return sp_frames; }
bool collide(Sprite &another_sprite);
virtual ~Sprite() {}
Sprite& operator=(Sprite other) {
std::swap(sp_pos, other.sp_pos);
std::swap(sp_vel, other.sp_vel);
std::swap(sp_img, other.sp_img);
std::swap(sp_center, other.sp_center);
std::swap(sp_size, other.sp_size);
std::swap(sp_radius, other.sp_radius);
std::swap(sp_animated, other.sp_animated);
std::swap(sp_frames, other.sp_frames);
std::swap(sp_cur_frame, other.sp_cur_frame);
return *this;
}
Sprite(const Sprite &obj)
{
sp_pos = obj.sp_pos;
sp_vel = obj.sp_vel;
sp_img = new SDL_Surface;
*sp_img = *obj.sp_img;
sp_center = obj.sp_center;
sp_size = obj.sp_size;
sp_radius = obj.sp_radius;
sp_animated = obj.sp_animated;
sp_frames = obj.sp_frames;
sp_cur_frame = obj.sp_cur_frame;
}
};
If it matters, I am using gcc compiler (Actually my IDE is codeblocks). The question is, why does the program crash? How do I solve this issue?
Thanks in advance
According to the topic Pushing local objects into a list
The full class:
class Invader
{
public:
Invader(const Invader&other);
Invader();
~Invader();
public:
void Init(InvaderTypes invadertype, CIw2DImage *AlienImage);
void Update(float dt);
void Render();
void SetAlienImage(CIw2DImage *image){ mImageAlien = image; }
void setVisible(bool show) { Visible = show; }
bool isVisible() const { return Visible; }
Iw2DSceneGraph::CSprite *AlienSprite;
Iw2DSceneGraph::CAtlas *AlienAtals;
CIw2DImage *mImageAlien;
std::list<Bullet*> *Bullets;
CIwFMat2D Transform; // Transform matrix
bool Visible; // Sprites visible state
bool Canfire;
};
Invader::Invader()
{
}
Invader::Invader(const Invader&other)
{ // Create EnemyTop atlas
AlienAtals = new CAtlas();
AlienSprite = new CSprite();
*AlienAtals = *other.AlienAtals;
*AlienSprite = *other.AlienSprite;
}
Invader::~Invader()
{
for (std::list<Bullet*>::iterator it = Bullets->begin(); it != Bullets->end(); ++it)
delete *it;
delete Bullets;
delete AlienAtals;
delete AlienSprite;
}
void Invader::Init(InvaderTypes invadertype, CIw2DImage *AlienImage)
{
if (invadertype == InvaderTypes::TOP_ALIEN)
{
//SetAlienImage(AlienImage);
mImageAlien = AlienImage;
// Create EnemyTop atlas
int frame_w = (int)(mImageAlien->GetWidth() / 2);
int frame_h = (int)(mImageAlien->GetHeight());
AlienAtals = new CAtlas(frame_w, frame_h, 2, mImageAlien);
AlienSprite = new CSprite();
AlienSprite->m_X = 0;
AlienSprite->m_Y = 0;
AlienSprite->SetAtlas(AlienAtals);
AlienSprite->m_W = (float)AlienAtals->GetFrameWidth();
AlienSprite->m_H = (float)AlienAtals->GetFrameHeight();
AlienSprite->m_AnchorX = 0.5;
AlienSprite->SetAnimDuration(2);
}
else if (invadertype == InvaderTypes::MIDDLE_ALIEN)
{
}
else if (invadertype == InvaderTypes::LAST_ALIEN)
{
}
Visible = true;
Bullets = new std::list<Bullet*>();
Canfire = true;
}
I added the objects by doing:
list<Invader> invaders;
int spacing = 10;
for (int i = 0; i < 5; i++)
{
Invader invader;
invader.Init(TOP_ALIEN, gameResources->getAlienImageTop());
invader.AlienSprite->m_X = 50 + spacing;
invaders.push_back(invader);
spacing += 50;
}
the usage: Right now it causes an access violation when accessing (it)
for (list<Invader>::iterator it = invaders.begin(); it != invaders.end(); it++)
{
(it)->Update(FRAME_TIME);
(it)->Render();
}
You can see the result here in the following image:
First, during Invader::Init()), you provide a copy of the pointer to AlienImage. Then when you copy the object into the std::list, you don't copy that same pointer to the new object. Further, during Invader::Invader(const Invader& other), you are not making a copy of Invader::Bullets. This leaves both the values uninitialized causing Undefined Behavior upon access (which probably occurs in (it)->Update(FRAME_TYIME); and/or (it)->Render(); -- I can't verify since you didn't provide a MCVE.