Solar System Simulator Physics Integration Issues (Unreal Engine 4, C++) - c++

So I'm making this solar system simulator in Unreal Engine 4 using C++ for a College project, however, I'm new to C++ and UE4 AND I suck at maths so I was in need of a bit of assistance, I wanted to use the Euler integrator for now just to get some basic physics in and have the Moon orbit around the Earth and then move on to probably use the Velocity Verlet method and build the whole Solar System that way. However, as of right now, even the Euler integration doesn't work. Here's the code in Moon.cpp
//Declare the masses
float MMass = 109.456;
float EMass = 1845.833;
//New velocities
float NewMVelX = 0.0;
float NewMVelY = 0.0;
float NewMVelZ = 0.0;
//Distance
float DistanceX = 0.0;
float DistanceY = 0.0;
float DistanceZ = 0.0;
//Earth's velocity
float EVelocityX = 0.0;
float EVelocityY = 0.0;
float EVelocityZ = 0.0;
//Moon's base velocity
float MVelocityX = 0.1;
float MVelocityY = 0.0;
float MVelocityZ = 0.0;
//Moon's acceleration
float MForceX = 0.0;
float MForceY = 0.0;
float MForceZ = 0.0;
//New position
float MPositionX = 0.0;
float MPositionY = 0.0;
float MPositionZ = 0.0;
// Called every frame
void AMoon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//Get Earth Location
FVector EPosition = FVector(0.0, 0.0, 0.0);
//Get Moon Location
FVector MPosition = GetActorLocation();
//Get the distance between the 2 bodies
DistanceX = (MPosition.X - EPosition.X) / 100;
DistanceY = (MPosition.Y - EPosition.Y) / 100;
//DistanceZ = MPosition.Z - EPosition.Z / 100;
//Get the acceleration/force for every axis
MForceX = G * MMass * EMass / (DistanceX * DistanceX);
MForceY = G * MMass * EMass / (DistanceY * DistanceY);
//MForceZ = G * MMass * EMass / (DistanceZ * DistanceZ);
//Get the new velocity
NewMVelX = MVelocityX + MForceX;
NewMVelY = MVelocityY + MForceY;
//NewMVelZ = MVelocityZ + MForceZ * DeltaTime;
//Get the new location
MPositionX = (MPosition.X) + NewMVelX;
MPositionY = (MPosition.Y) + NewMVelY;
//MPositionZ = MPosition.Z * (MVelocityZ + NewMVelZ) * 0.5 * DeltaTime;
//Set the new velocity on the old one
MVelocityX = NewMVelX;
MVelocityY = NewMVelY;
//MVelocityZ = NewMVelZ;
//Assign the new location
FVector NewMPosition = FVector(MPositionX, MPositionY, MPositionZ);
//Set the new location
SetActorLocation(NewMPosition);
}
The values might not be right, I was just making tests at this point. I based this code on the different pieces of information I got on Google and multiple websites but at this point I'm quite confused. What is happening is that the Moon just starts going in 1 direction and never stops. I know my issue is with the force/acceleration/actual gravity of the Earth, it should pull the Moon not push it away. But anyway, if anyone has an idea what I'm doing wrong, I'd be very grateful to hear what you have to say! Thanks

The force depends on the euclidean, rotation-invariant distance. Thus use
distance = sqrt(distanceX²+distanceY²+distanceZ²)
force = - G*Emass*Mmass/distance²
forceX = force * X/distance
forceY = force * Y/distance
forceZ = force * Z/distance
The time stepping of the velocity is also wrong, it should be
velocityX += forceX/Mmass * deltaTime
velocityY += forceY/Mmass * deltaTime
velocityZ += forceZ/Mmass * deltaTime
and of course also the position update contains the time step
positionX += velocityX * deltaTime
....

Related

Calibrating the output(output factor) of a smaller source using Geant4/GATE MonteCarlo simulation

I am using GATE(which uses Geant4) to do MC studies on dosimetric output. I am using a cylindrical cobalt source at 80 cm SAD to measure the PDD in a water phantom and dose at depth of 10 cm.
I now want to simulate a smaller source (say, r/2 and h/2) and compare the dosimetric output at a depth of 10 cm. Besides the geometry, I see that I am able to control the number of particles and time of the simulation. What would be the best way to change these two parameters to mimic the lower output from a smaller source? Or is there any other parameter that can be changed to mimic a smaller source? I am trying to calculate the output factor of the smaller source w.r.t. to the original source.
Not sure if it helps, this is cylindrical source with Co60
Source::Source():
_particleGun{nullptr},
_sourceMessenger{nullptr},
_radius{-1.0},
_halfz{-1.0},
_nof_particles{10}
{
_particleGun = new G4ParticleGun( 1 );
G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
G4String particleName = "gamma"; // "geantino"
_particleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
_particleGun->SetParticlePosition(G4ThreeVector(0., 0., 0.));
_particleGun->SetParticleMomentumDirection(G4ThreeVector(0., 0., 1.));
_particleGun->SetParticleEnergy(1000.0*MeV);
_sourceMessenger = new SourceMessenger(this);
}
Source::~Source()
{
delete _particleGun;
delete _sourceMessenger;
}
troika Source::sample_direction()
{
double phi = 2.0 * M_PI * G4UniformRand();
double cos_z = 2.0 * G4UniformRand() - 1.0;
double sin_z = sqrt( (1.0 - cos_z) * (1.0 + cos_z) );
return troika{ sin_z * cos(phi), sin_z * sin(phi), cos_z };
}
double Source::sample_energy()
{
return (G4UniformRand() < P_lo) ? E_lo : E_hi;
}
void Source::GeneratePrimaries(G4Event* anEvent)
{
for(int k = 0; k != _nof_particles; ++k) // we generate _nof_particles at once
{
// here we sample spatial decay vertex uniformly in the cylinder
double z = _halfz * ( 2.0*G4UniformRand() - 1.0 );
double phi = 2.0 * M_PI * G4UniformRand();
double r = _radius * sqrt(G4UniformRand());
auto x = r * cos(phi);
auto y = r * sin(phi);
_particleGun->SetParticlePosition(G4ThreeVector(x, y, z));
// now uniform-on-the-sphere direction
auto dir = sample_direction();
_particleGun->SetParticleMomentumDirection(G4ThreeVector(dir._wx, dir._wy, dir._wz));
// energy 50/50 1.17 or 1.33
auto e = sample_energy();
_particleGun->SetParticleEnergy(e);
// all together in a vertex
_particleGun->GeneratePrimaryVertex(anEvent);
}
}

How to scale the rotation of a quaternion

I am trying to do the equivalent of multiplying the velocity by the time between frames. I would imagine that doing this for quaternions would be done by raising them to a power. I have code to rotate an object based on my mouse movements. It has a main loop running at one frame rate and a physics loop running at a fixed frame rate. Here is the relevant part of the main loop:
glfwPollEvents();
Input::update();
window.clear(0,0,0,1);
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().x, glm::vec3(0,1,0));
rigidBody.angularVelocity *= glm::angleAxis(0.001f * Input::deltaMouse().y, glm::vec3(1,0,0));
if(Input::getKey(Input::KEY_A))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_D))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(1,0,0);
}
if(Input::getKey(Input::KEY_W))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_S))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,0,1);
}
if(Input::getKey(Input::KEY_LCONTROL))
{
rigidBody.velocity -= float(Time::getDelta()) * glm::vec3(0,1,0);
}
if(Input::getKey(Input::KEY_LSHIFT))
{
rigidBody.velocity += float(Time::getDelta()) * glm::vec3(0,1,0);
}
Here is the relevant part of the physics loop:
for(int i = 0; i < *numRigidBodies; i++)
{
rigidBodies[i].transform->getPos() += rigidBodies[i].velocity;
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
}
rigidBodies[0].angularVelocity = glm::quat();
rigidBodies[0].velocity = glm::vec3();
This works fine, but when I try raising angular velocity to a power with glm::pow, the object rotates randomly and does not follow my mouse. I realize I could do this with a line of code like
rigidBodies[i].transform->getRot() *= glm::angleAxis((float)Time::getFixedDelta() * glm::angle(rigidBodies[i].angularVelocity), glm::axis(rigidBodies[i].angularVelocity));
but this seems needlessly complicated for the task. What is causing this issue, and how can I fix it?
Not sure exactly how to do it with the API you're using, but basically, you would use Quaternion::Slerp(). Slerp means "spherical linear interpolation".
Something like this(pseudocode) should work:
auto& rot = rigidBodies[i].transform->getRot();
auto goal = rigidBodies[i].angularVelocity * rot;
rot = rot.slerp(rot, goal, Time::deltaTime);
Edit:
I should note that this is not how I would approach this problem. I would just store the rotation around the X and Y axis as scalars and construct a new quaternion from them each frame.
Please excuse the sloppy pseudo code:
// previous x and y positions, could probably be set in MouseDown event
float lastX = ...;
float lastY = ...;
float xRotation = 0;
float yRotation = 0;
float rotationSpeed = 1.0;
void OnMouseMove(float x, float y) {
float dx = x - lastX;
float dy = y - lastY;
lastX = x;
lastY = y;
xRotation += dy * rotationSpeed * Time::deltaTime;
yRotation += dx * rotationSpeed * Time::deltaTime;
rigidBodies[i].transform->getRot() = eulerQuat(xRotation, yRotation, 0);
}
Turns out angular velocity is usually represented as a 3d vector where the direction is the axis and the magnitude is the angular speed. Replace this line of code:
rigidBodies[i].transform->getRot() *= rigidBodies[i].angularVelocity;
with this:
if(rigidBodies[i].angularVelocity != glm::vec3())
rigidBodies[i].transform->getRot() *= glm::quat(rigidBodies[i].angularVelocity * float(Time::getFixedDelta()));
and the physics system works as expected. The if check makes sure that angular speed is not 0.

Inverting an angle on the Y-axis, X works

I've asked this question over at GameDev but got not response so far and this question is a bit time sensitive unfortunately.
I'm pretty sure this is just me doing something stupid or not understanding something that I should but I cannot figure out what is wrong here.
I'm having a problem bouncing a projectile off a sprite, we've been asked to move the projectile using the equations of motions which makes things a little more difficult but as far as I can see what I have should work.
What I'm trying to do is change the angle of the collided projectile depending on which direction it is coming from.
Here is a video that is hopefully not too laggy for you to see what is happening:
Link
When the projectile collides with the left or right hand side of the sprite everything works as expected, it just switches X direction.
When it hit's the top or bottom of the sprite however it doesn't change, it just sort of rolls along the top and the shoots off.
Here is the movement code:
float nX = get_x() + cos(nGetAngle() * 3.14 / 180) * getU() * getT();
float nY = get_y() - sin(nGetAngle() * 3.14 / 180) * getU() * getT() + 0.5 * 9.8 * getT() * getT();
set_world_position(nX, nY);
Where U is initial velocity, T is time and nGetAngle() is the angle in degrees (which is set to radians whenever the angle is set).
Here is my collision for the top of the player:
//if the projectile is colliding in any way with the player sprite
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
and here is my collision for the left of the player:
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
The left side collision works, the top does not and I have no idea why.
If necessary I can post the entire project somewhere.
Full collision code for player:
void Game::playerCollision()
{
if (projectiles[currProj]->bb_collision(player))
{
if (projectiles[currProj]->get_y() < player->get_y()) // top of player
{
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
projectiles[currProj]->set_world_position_y(player->get_y() - projectiles[currProj]->get_height() - 1);
}
else if (projectiles[currProj]->get_y() + projectiles[currProj]->get_height() > player->get_y() + player->get_height() + 1) // bottom of player
{
projectiles[currProj]->set_world_position_y(player->get_y() + player->get_height());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(-vy, vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() < player->get_x()) // left of player
{
projectiles[currProj]->set_world_position_x(player->get_x() - projectiles[currProj]->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
else if (projectiles[currProj]->get_x() > player->get_x()) // right of player
{
projectiles[currProj]->set_world_position_x(player->get_x() + player->get_width());
float vx = cos(projectiles[currProj]->nGetAngle());
float vy = sin(projectiles[currProj]->nGetAngle());
float newAngle = atan2(vy, -vx) * 180 / 3.14;
projectiles[currProj]->nSetAngle(newAngle);
}
}
}
I think your collision detection is not sufficient. without knowing your representation in detail
you do not check where the projectile (pr) came from. a collision top left within the player (pl) might have entered through the top or from the left
you do not bounce the pr immediately, you just alter the direction. depending on the entry depth it might not be able to exit with the next iteration. this happens especially on the top where the pr accelerates downwards but slows down upwards.
so you must
detect the entry surface (determines angle)
and most important rebounce immediately

How to change the direction of a moving sprite at screen edges?

I am using this code in cocos2d-x for shooting in specific directions but at the edge of screen my sprite(i.e. my shooting bubbles) used to get lost. I need help in making it to change its angle when it collides at screen edges and change direction to go up.
Code in c++ using cocos2dx:-
CCSize winSize = CCDirector::sharedDirector()->getVisibleSize();
CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
// Determinie offset of location to projectile
float offX = flocationX - m_pOwner->getPosition().x;
float offY = flocationY - m_pOwner->getPosition().y;
// Bail out if we are shooting down or backwards
if (offY <= 0) return;
// Ok to add now - we've double checked position
// Determine where we wish to shoot the projectile to
//float realX = origin.x + winSize.width + (m_pOwner->getPosition().x);
float realY = origin.y + winSize.height + (m_pOwner->getPosition().y);
// float ratio = offY / offX;
float ratio = offX / offY;
//float realY = (realX * ratio) + m_pOwner->getPosition().y;
float realX = (realY * ratio) + m_pOwner->getPosition().x;
CCPoint realDest = ccp(realX, realY);
// Determine the length of how far we're shooting
float offRealX = realX - m_pOwner->getPosition().x;
float offRealY = realY - m_pOwner->getPosition().y;
float length = sqrtf((offRealX * offRealX) + (offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Move projectile to actual endpoint
m_pOwner->runAction( CCSequence::create(
CCMoveTo::create(realMoveDuration, realDest),
CCCallFuncN::create(getOwner()->getParent()->getComponent("SceneController"),
callfuncN_selector(SceneController::spriteMoveFinished)),
NULL) );

Am I converting local space to world space coordinates properly?

I'm trying to create a bone and IK system. Below is the method that is recursive and that calculates the absolute positions and absolute angles of each bone. I call it with the root bone and zero'd parameters. It works fine, but when I try to use CCD IK I get discrepancies between the resulting end point and the calculated one. Therefore maybe I'm doing this wrong even though it works.
Thanks
void Skeleton::_updateBones( Bone* root,float realStartX, float realStartY, float realStartAngle )
{
if(!root->isRelative())
{
realStartX = 0.0f;
realStartY = 0.0f;
realStartAngle = 0.0f;
}
realStartX += root->getX();
realStartY += root->getY();
realStartAngle += root->getAngle();
float vecX = sin(realStartAngle);
float vecY = cos(realStartAngle);
realStartX += (vecX * root->getLength());
realStartY += (vecY * root->getLength());
root->setFrame(realStartX,realStartY,realStartAngle);
float angle = fmod(realStartAngle,2.0f * 3.141592f);
if( angle < -3.141592f )
angle += (2.0f * 3.141592);
else if( angle > 3.141592f )
angle -= (2.0f * 3.141592f);
for(std::list<Bone>::iterator it = root->begin(); it != root->end(); ++it)
{
_updateBones(&(*it),realStartX,realStartY,angle);
}
}
This looks wrong.
float vecX = sin(realStartAngle);
float vecY = cos(realStartAngle);
Swap sin() and cos().
float vecX = cos(realStartAngle);
float vecY = sin(realStartAngle);