I'm working on a small game in UE4 and hit a small issue.
So far, I've got the player dealing damage to enemies (via raycast/line trace) all in C++. Now I'm trying to make the enemy cause damage to the player when their collision boxes overlap.
The player uses the TakeDamage() function as does the enemy to deal damage to each other.
Currently, the enemy does deal damage to the player with this Blueprint setup when both of the collisons overlap.
I'm trying to translate this into C++.
I have been looking at many websites which say to use either OnActorBeginOverlap() or OnComponentBeginOverlap() with the AddDynamic() function. However, when trying to call OnActorBeginOverlap() it doesn't appear when I do BoxCollider-> but OnComponentBeginOverlap() does, but does not have the AddDynamic() function. From what I understand, AddDynamic is a macro which I'm not too familiar with. OnActorBeginOverlap() does appear without the BoxCollider->. Both objects are Actors.
Any ideas?
Code Example:
BasicZombie.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/BoxComponent.h"
#include "BasicZombie.generated.h"
UCLASS()
class MERCENARIES_API ABasicZombie : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABasicZombie();
void Destroy();
UPROPERTY(EditAnywhere)
UBoxComponent* BoxCollider;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
private:
UPROPERTY(EditDefaultsOnly)
float maxHealth = 100.0f;
UPROPERTY(VisibleAnywhere)
float health;
void DealDamage();
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
BasicZombie.cpp
#include "BasicZombie.h"
#include "MainCharacter.h"
#include "GameFramework/Actor.h"
#include "DrawDebugHelpers.h"
#include "Engine/Engine.h"
// Sets default values
ABasicZombie::ABasicZombie()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
BoxCollider = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxCollider"));
BoxCollider->OnComponentBeginOverlap.AddDynamic(this, &AMainCharacter::OnCollision);
health = maxHealth;
}
// Called when the game starts or when spawned
void ABasicZombie::BeginPlay()
{
Super::BeginPlay();
}
float ABasicZombie::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
DamageToApply = FMath::Min(health, DamageToApply);
health -= DamageToApply;
UE_LOG(LogTemp, Warning, (TEXT("Health Remaining: %f")), health);
//GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("Health Remaining: %f"), health));
Destroy();
return DamageToApply;
}
//void ABasicZombie::DealDamage()
//{
//
//}
void ABasicZombie::Destroy()
{
if (health <= 0)
{
UWorld* WorldRef = GetWorld();
AMainCharacter* mainCharacter = Cast<AMainCharacter>(WorldRef->GetFirstPlayerController()->GetCharacter());
mainCharacter->currentScore += 500;
GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Green, FString::Printf(TEXT("Score: %i"), mainCharacter->currentScore));
AActor::Destroy();
}
}
// Called every frame
void ABasicZombie::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
MainCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "MainCharacter.generated.h"
class AHandgun;
UCLASS()
class MERCENARIES_API AMainCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMainCharacter();
void OnCollision(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& Hit);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
private:
UPROPERTY(EditDefaultsOnly)
TSubclassOf<AHandgun> HandgunClass;
UPROPERTY()
AHandgun* Handgun;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//Handles Input for moving FORWARD and BACK
UFUNCTION()
void MoveForward(float value);
//Handles input for moving RIGHT and LEFT
UFUNCTION()
void MoveRight(float value);
UFUNCTION()
void Shoot();
UFUNCTION()
void printHealth();
//FPS camera
UPROPERTY(VisibleAnywhere)
UCameraComponent* MainCharacterCameraComponent;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
UPROPERTY()
int32 playerScore;
UPROPERTY()
int32 currentScore;
UPROPERTY()
float maxHealth = 1000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float currentHealth;
};
MainCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "MainCharacter.h"
#include "Engine/Engine.h"
#include "GameFramework/Actor.h"
#include "Handgun.h"
#include "GameFramework/Character.h"
#include "Components/SkeletalMeshComponent.h"
// Sets default values
AMainCharacter::AMainCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//Create a first person camera component
MainCharacterCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
check(MainCharacterCameraComponent != nullptr);
//Attach the camera component to our capsule component
MainCharacterCameraComponent->SetupAttachment(CastChecked<USceneComponent, UCapsuleComponent>(GetCapsuleComponent()));
//Position the camera slightly above the eyes
MainCharacterCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
//Enable the pawn to control camera rotation
MainCharacterCameraComponent->bUsePawnControlRotation = true;
currentHealth = maxHealth;
playerScore = currentScore;
}
// Called when the game starts or when spawned
void AMainCharacter::BeginPlay()
{
Super::BeginPlay();
check(GEngine != nullptr);
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("[MainCharacter DEBUG] - MainCharacter in use."));
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("[MainCharacter DEBUG] - Current Score: %f"), playerScore));
Handgun = GetWorld()->SpawnActor<AHandgun>(HandgunClass);
Handgun->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("WeaponSocket"));
Handgun->SetOwner(this);
}
// Called every frame
void AMainCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//printHealth();
}
// Called to bind functionality to input
void AMainCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
//Set up movement bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AMainCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AMainCharacter::MoveRight);
//Set up Look Bindings
PlayerInputComponent->BindAxis("Turn", this, &AMainCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AMainCharacter::AddControllerPitchInput);
//Setup Weapon Shooting
PlayerInputComponent->BindAction(TEXT("Fire"), EInputEvent::IE_Pressed, this, &AMainCharacter::Shoot);
PlayerInputComponent->BindAction(TEXT("printHealth"), EInputEvent::IE_Pressed, this, &AMainCharacter::printHealth);
}
void AMainCharacter::Shoot()
{
Handgun->Shoot();
}
void AMainCharacter::printHealth()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, FString::Printf(TEXT("[MainCharacter DEBUG] - Current Health: %f"), currentHealth));
}
float AMainCharacter::TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser)
{
float DamageToApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
DamageToApply = FMath::Min(currentHealth, DamageToApply);
currentHealth -= DamageToApply;
GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf(TEXT("Health Remaining: %f"), currentHealth));
if (currentHealth <= 0)
{
GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, TEXT("You're Dead!"));
}
return DamageAmount;
}
void AMainCharacter::MoveForward(float value)
{
//Find out which way is "forward" and reocrd that the player wants to move that way
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction, value);
}
void AMainCharacter::MoveRight(float value)
{
//Find out which way is "right" and record that the player wants to move that way
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, value);
}
However, the BoxCollider->OnComponentBeginOverlap.AddDynamic(this, &AMainCharacter::OnCollision); in BasicZombie.cpp has a red line under AddDynamic.
I am in a Discord Server for UE4 development, but I haven't had much luck getting any help from there.
Can I see your C++ code? Are you calling AddDynamic from the UBoxComponent?
Try this
UPROPERTY(EditAnywhere)
UBoxComponent* BoxCollider = nullptr;
BoxCollider = GetOwner()->FindComponentByClass<UBoxComponent>();
BoxCollider->OnComponentBeginOverlap.AddDynamic(this, &YourActor::OnCollision);
Use this as your OnCollision signature
void OnCollision(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComponent, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& Hit);
Also when I was learning unreal engine I dealt with a lot of pitfalls. Try to find a community of unreal engine developers and learn with them.
Related
I am working with Unreal Engine 4.9 and I am creating a rolling ball game using mostly C++, I am avoiding using Blueprints.
What I am trying to do is put in place some checkpoints where the player can spawn if they fall off the map (aka. they die).
I have an actor which is the box that represents the checkpoint and a pawn which represents the rolling ball.
So far, I was able to put in place an overlap function so that when the ball overlaps with the box (BoxActor.cpp), it prints out the location of the box and a debug message coming from the pawn (TestBallBall.cpp).
For BoxActor.cpp, this is the code I have so far:
#include "BoxActor.h"
#include "Components/BoxComponent.h"
#include "Engine/Engine.h"
#include "EngineGlobals.h"
#include "Engine.h"
#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
// Sets default values
ABoxActor::ABoxActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
CollisionBox = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComponent"));
CollisionBox->SetBoxExtent(FVector(32.f, 32.f, 32.f));
CollisionBox->SetCollisionProfileName("Trigger");
RootComponent = CollisionBox;
CollisionBox->OnComponentBeginOverlap.AddDynamic(this, &ABoxActor::OnOverlapBegin);
CollisionBox->OnComponentEndOverlap.AddDynamic(this, &ABoxActor::OnOverlapEnd);
}
// Called when the game starts or when spawned
void ABoxActor::BeginPlay()
{
Super::BeginPlay();
Ball = Cast<ATestBallBall>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));
}
void ABoxActor::OnOverlapBegin(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
Ball->AddDebugMessage();
Location = this->GetActorLocation();
GEngine->AddOnScreenDebugMessage(-1, 1.f, FColor::Red, FString::Printf(TEXT("Checkpoint location is: %s"), *Location.ToString()));
}
void ABoxActor::OnOverlapEnd(AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
}
For my TestBallBall.cpp, this is what I have so far:
#include "TestBallBall.h"
#include "Engine/Engine.h"
#include "EngineGlobals.h"
#include "Engine.h"
ATestBallBall::ATestBallBall()
{
static ConstructorHelpers::FObjectFinder<UStaticMesh> BallMesh(TEXT("/Game/Rolling/Meshes/BallMesh.BallMesh"));
// Create mesh component for the ball
Ball = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Ball0"));
Ball->SetStaticMesh(BallMesh.Object);
Ball->BodyInstance.SetCollisionProfileName(UCollisionProfile::PhysicsActor_ProfileName);
Ball->SetSimulatePhysics(true);
Ball->SetAngularDamping(0.1f);
Ball->SetLinearDamping(0.01f);
Ball->BodyInstance.MassScale = 3.5f;
Ball->BodyInstance.MaxAngularVelocity = 800.0f;
Ball->SetNotifyRigidBodyCollision(true);
RootComponent = Ball;
// Create a camera boom attached to the root (ball)
SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArm0"));
SpringArm->AttachTo(RootComponent);
SpringArm->bDoCollisionTest = false;
SpringArm->bAbsoluteRotation = true; // Rotation of the ball should not affect rotation of boom
SpringArm->RelativeRotation = FRotator(-45.f, 0.f, 0.f);
SpringArm->TargetArmLength = 1200.f;
SpringArm->bEnableCameraLag = false;
SpringArm->CameraLagSpeed = 3.f;
// Create a camera and attach to boom
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera0"));
Camera->AttachTo(SpringArm, USpringArmComponent::SocketName);
Camera->bUsePawnControlRotation = false; // We don't want the controller rotating the camera
// Set up forces
RollTorque = 30000000.0f;
JumpImpulse = 500000.0f;
bCanJump = true; // Start being able to jump
jumps = 0;
}
void ATestBallBall::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
// set up gameplay key bindings
InputComponent->BindAxis("MoveRight", this, &ATestBallBall::MoveRight);
InputComponent->BindAxis("MoveForward", this, &ATestBallBall::MoveForward);
InputComponent->BindAction("Jump", IE_Pressed, this, &ATestBallBall::Jump);
}
void ATestBallBall::MoveRight(float Val)
{
const FVector Torque = FVector(-1.f * Val * RollTorque, 0.f, 0.f);
Ball->AddTorque(Torque);
}
void ATestBallBall::MoveForward(float Val)
{
const FVector Torque = FVector(0.f, Val * RollTorque, 0.f);
Ball->AddTorque(Torque);
}
void ATestBallBall::Jump()
{
// Allow only 2 jumps
if(bCanJump)
{
if (jumps < 2){
const FVector Impulse = FVector(0.f, 0.f, JumpImpulse);
Ball->AddImpulse(Impulse);
jumps++;
}
else
bCanJump = false;
}
}
void ATestBallBall::NotifyHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit)
{
Super::NotifyHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse, Hit);
bCanJump = true;
jumps = 0;
}
void ATestBallBall::AddDebugMessage()
{
UE_LOG(LogActor, Warning, TEXT("AddDebugMessage Function On ATestBallBall Called"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, "AddDebugMessage Function On ATestBallBall Called");
}
Thank you in advance for your help and sorry for the long post.
According to the unreal documentation, GetRelativeRotation is a member variable of USceneComponent. However, when I run my code, it tells me that it's not. I have tried switching my Camera to a UCameraComponent(as it was originally before I checked the documenation) and still no luck. Any ideas? heres the code for the .cpp file:
#include "FirstPersonCharacter.h"
AFirstPersonCharacter::AFirstPersonCharacter()
{
PrimaryActorTick.bCanEverTick = true;
AutoPossessPlayer = EAutoReceiveInput::Player0;
bUseControllerRotationYaw = false;
Cam = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
Cam->AttachTo(RootComponent);
Cam->SetRelativeLocation(FVector(0.f, 0.f, 40.f)); //places the camera 40cm above the centre of the body.
}
void AFirstPersonCharacter::BeginPlay()
{
Super::BeginPlay();
}
void AFirstPersonCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AFirstPersonCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
InputComponent->BindAxis("HorizontalMovement", this, &AFirstPersonCharacter::HorizontalMove);
InputComponent->BindAxis("VerticalMovement", this, &AFirstPersonCharacter::VerticalMove);
InputComponent->BindAxis("VerticalLook", this, &AFirstPersonCharacter::VerticalRot);
InputComponent->BindAxis("HorizontalLook", this, &AFirstPersonCharacter::HorizontalRot);
}
void AFirstPersonCharacter::HorizontalMove(float value)
{
if (value) //make sure it doesn't constantly call on addmovementinput if theres no value to add to it
{
AddMovementInput(GetActorRightVector(), value);
}
}
void AFirstPersonCharacter::VerticalMove(float value)
{
if (value) //make sure it doesn't constantly call on addmovementinput if theres no value to add to it
{
AddMovementInput(GetActorForwardVector(), value);
}
}
void AFirstPersonCharacter::HorizontalRot(float value)
{
if (value)
{
AddActorLocalRotation(FRotator(0, value, 0));
}
}
void AFirstPersonCharacter::VerticalRot(float value)
{
if (value)
{
float temp = Cam->GetRelativeRotation().Pitch + value; //THIS IS WHERE THE ISSUE IS
if (temp < 65 && temp > -65)
{
Cam->AddLocalRotation(FRotator(value, 0, 0));
}
}
and the code for the header file:
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Components/InputComponent.h"
#include "Components/SceneComponent.h"
#include "Camera/CameraComponent.h"
#include "FirstPersonCharacter.generated.h"
UCLASS()
class MOMENTUMPROJECTMAIN_API AFirstPersonCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AFirstPersonCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
private:
void HorizontalMove(float value);
void VerticalMove(float value);
void HorizontalRot(float value);
void VerticalRot(float value);
//allows me to edit in the editor, without going into source code.
UPROPERTY(EditAnywhere, Category = "Camera")
UCameraComponent* Cam;
};
thanks.
I am using Unreal Engine version 4.25. I have created an actor and placed it on a blank template. The actor is visible in the editor but becomes invisible once the play button is hit. I am trying to move the object in circles with my code.
Here's the code:
MyActor.cpp
#include "MyActor.h"
// Sets default values
AMyActor::AMyActor()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Dimensions = FVector(10, 0, 0);
AxisVector = FVector(1, 0, 0);
Location = FVector(1, 0, 0);
Multiplier = 50.f;
}
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// Updates the angle of the object
AngleAxis += DeltaTime * Multiplier;
if (AngleAxis >= 360.0f)
{
AngleAxis = 0;
}
//Rotates around axis
FVector RotateValue = Dimensions.RotateAngleAxis(AngleAxis, AxisVector);
Location.X += RotateValue.X;
Location.Y += RotateValue.Y;
Location.Z += RotateValue.Z;
SetActorLocation(Location, false, 0, ETeleportType::None);
}
MyActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"
UCLASS()
class ROBOTEURS_API AMyActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// declare our float variables
UPROPERTY(EditAnywhere, Category = Movement)
float AngleAxis;
UPROPERTY(EditAnywhere, Category = Movement)
FVector Dimensions;
UPROPERTY(EditAnywhere, Category = Movement)
FVector AxisVector;
UPROPERTY(EditAnywhere, Category = Movement)
FVector Location;
UPROPERTY(EditAnywhere, Category = Movement)
float Multiplier;
};
What could have gone wrong?
This fixed it, I did not create a mesh:
https://www.youtube.com/watch?v=30XEdBoPw6c
I added the following:
.cpp
AMyActor::AMyActor()
{
...
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
RootComponent = Root;
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
Mesh->AttachTo(Root);
...
}
.h
public:
...
UPROPERTY()
USceneComponent* Root;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* Mesh;
...
I want to open the door when pressing left mouse button, but nothing happens
OpenDoor.h
UCLASS()
class HOME_API ADoorOpen : public APawn
{
GENERATED_BODY()
public:
ADoorOpen();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
UPROPERTY()
USceneComponent* Root;
UPROPERTY(EditAnywhere)
UStaticMeshComponent* Door;
UPROPERTY(EditAnywhere, Category = "DoorOpen")
float rotateValue;
UPROPERTY(BlueprintReadOnly, Category = "DoorOpen")
FRotator doorRotation;
UPROPERTY(BlueprintReadOnly, Category = "DoorOpen")
bool open;
UFUNCTION(BlueprintCallable, Category = "DoorOpen")
void OpenCloseDoor();
};
OpenDoor.cpp
#include "DoorOpen.h"
#include "DrawDebugHelpers.h"
#include "Engine.h"
ADoorOpen::ADoorOpen()
{
PrimaryActorTick.bCanEverTick = true;
rotateValue = 0.0f;
open = false;
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
RootComponent = Root;
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Door"));
Door->SetRelativeLocation(FVector(0.f, 50.f, -50.f));
Door->SetupAttachment(RootComponent);
}
void ADoorOpen::OpenCloseDoor()
{
// don't see this while playing
GEngine->AddOnScreenDebugMessage(-1, 0.f, FColor::Red, "OpenCloseDoor");
//...
}
void ADoorOpen::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Can't call this
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ADoorOpen::OpenCloseDoor);
}
Increase the display time for AddOnScreenDebugMessage
GEngine->AddOnScreenDebugMessage(-1, 1.0f, FColor::Red, "OpenCloseDoor");
Otherwise, the text will show for just a frame.
I just got back into coding. Trying to make a simple breakout game, I did start of just making a simple 'pong' game but found it quite easy so I am trying to expand it to a breakout game (image attached for those who do not know what it is).
To handle the blocks at the top of the screen I have used a vector of blocks, from which right now I am trying to draw them onto the screen. I am unable to do this as I am getting an error:
error C2664: 'void sf::RenderTarget::draw(const sf::Vertex
*,size_t,sf::PrimitiveType,const sf::RenderStates &)' : cannot convert argument 1 from 'Block' to 'const sf::Drawable &'
which is inside the block.cpp file
Here is the relevant code, there are more functions but they do not apply to this. Sorry for any bad code in there :)
block.cpp
Block::Block(float startX, float startY)
{
position.x = startX;
position.y = startY;
colour = sf::Color::White;
block.setSize(sf::Vector2f(width, height));
block.setFillColor(colour);
block.setPosition(position);
}
void Block::draw(Block block, sf::RenderWindow& window)
{
window.draw(block);
}
blockContainer.cpp
#pragma once
class ContainerOfBlocks
{
public:
ContainerOfBlocks(int useless);
~ContainerOfBlocks();
std::vector<Block> getContainer();
void drawContainer(sf::RenderWindow& window);
private:
std::vector<Block> blockContainer;
};
blockContainer.h
#pragma once
class ContainerOfBlocks
{
public:
ContainerOfBlocks(int useless);
~ContainerOfBlocks();
std::vector<Block> getContainer();
void drawContainer(sf::RenderWindow& window);
private:
std::vector<Block> blockContainer;
};
Thank you for any help :)
(I'll have to put it here because I don't have enough reputation yet to comment)
some things
I don't understand why you have this code void Block::draw(Block block, sf::RenderWindow &window). It should be void Block::draw(sf::RenderWindow &window) and then just draw block (which is a class member) OR pass block by reference if you want to draw the block from somewhere else
In any case, Block should inherit from sf::Drawable and use its function to draw. I think that's what the error message is saying. For example class Block : public sf::Drawable { ... }; and the function to draw would be virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const; in the header and void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const { renderTarget.draw(block); } in your .cpp. Then you can iterate over the vector of blocks you already have and draw each block
the function std::vector<Block> getContainer() should return a reference to the vector (std::vector<Block> &getContainer())
it's not an error but I prefer using #ifndef ... #define... #endif header guard instead of #pragma once
edit (regarding your replies below):
I made a quick project that uses most of your code.
(Also make sure to read my notes below the code)
Here's a picture of what it looks like compiled:
Code:
block.h
#ifndef BLOCK_H_INCLUDED
#define BLOCK_H_INCLUDED
#include <SFML/Graphics.hpp>
class Block : public sf::Drawable {
public:
Block();
Block::Block(float startX, float startY);
virtual ~Block();
private:
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
sf::RectangleShape block;
sf::Vector2f position;
float width;
float height;
sf::Color colour;
};
#endif
block.cpp
#include "block.h"
Block::Block() :
position(sf::Vector2f()),
width(40.0f),
height(20.0f),
colour(sf::Color())
{
}
Block::Block(float startX, float startY) :
width(40.0f),
height(20.0f)
{
position.x = startX;
position.y = startY;
colour = sf::Color::White;
block.setSize(sf::Vector2f(width, height));
block.setFillColor(colour);
block.setPosition(position);
}
Block::~Block() {
}
void Block::draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const {
renderTarget.draw(block);
}
blockContainer.h
#ifndef BLOCKCONTAINER_H_INCLUDED
#define BLOCKCONTAINER_H_INCLUDED
#include "block.h"
class ContainerOfBlocks : public sf::Drawable {
public:
ContainerOfBlocks();
ContainerOfBlocks(int useless, const sf::Vector2f pos);
~ContainerOfBlocks();
std::vector<Block> &getContainer();
void drawContainer(sf::RenderWindow &window);
private:
virtual void draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const;
std::vector<Block> blockContainer;
};
#endif
blockContainer.cpp
#include "blockContainer.h"
ContainerOfBlocks::ContainerOfBlocks() {
}
ContainerOfBlocks::ContainerOfBlocks(int useless, const sf::Vector2f pos) {
if (useless > 0) {
float x = pos.x;
float y = pos.y;
for (std::size_t i = 0; i < static_cast<std::size_t>(useless); ++i) {
blockContainer.push_back(Block(x, y));
x += 50.0f;
}
}
}
ContainerOfBlocks::~ContainerOfBlocks() {
}
std::vector<Block> &ContainerOfBlocks::getContainer() {
return blockContainer;
}
void ContainerOfBlocks::drawContainer(sf::RenderWindow &window) {
for (std::size_t i = 0; i < blockContainer.size(); ++i) {
window.draw(blockContainer[i]);
}
}
void ContainerOfBlocks::draw(sf::RenderTarget &renderTarget, sf::RenderStates renderStates) const {
for (std::size_t i = 0; i < blockContainer.size(); ++i) {
renderTarget.draw(blockContainer[i]);
}
}
main.cpp
#include <SFML/Graphics.hpp>
#include "block.h"
#include "blockContainer.h"
int main() {
sf::RenderWindow window(sf::VideoMode(400, 200), "SFML works!");
window.setFramerateLimit(30);
window.setVerticalSyncEnabled(false);
// create container with 5 blocks in it, starting at pos 10/10
// this container will be drawn using ContainerOfBlocks' void drawContainer(sf::RenderWindow &window)
ContainerOfBlocks testBlocks(5, sf::Vector2f(10.0f, 10.0f));
// create another container, starting at pos 10/50
// this one will be drawn using sf::Drawable's function to draw
ContainerOfBlocks testBlocks2(5, sf::Vector2f(10.0f, 50.0f));
while (window.isOpen()) {
sf::Event evt;
while (window.pollEvent(evt)) {
if (evt.type == sf::Event::Closed) {
window.close();
}
}
window.clear();
testBlocks.drawContainer(window);
window.draw(testBlocks2);
window.display();
}
return 0;
}
As you can see Block now inherits from sf::Drawable and can be drawn with xxx.draw(block).
BlockContainer now has two different functions to draw its contents (this is only for demonstrating purposes, you only need one function to draw depending on what you like better). If you want to keep your own drawing function, you can remove the : public sf::Drawable from BlockContainer.
In main() two block containers are created, one (testBlocks) will be drawn using BlockContainer's void drawContainer(sf::RenderWindow &window) draw function from your original code, the other (testBlocks2) using sf::Drawable's.
Also note how &getContainer() now returns a reference to the vector of blocks. If you don't return a reference the original vector won't be affected by whatever you want to do to it from the outside.