Jump to content

GravityGun realization


photo

Recommended Posts

I working on "gravity gun" realization (this functional allow users take and carry physical game objects). I have some base realization and some questions :), so I start from code, that I already have:

PlayerActor actor;			// player

Object mouseSelectedObject;		// object, that will be carried

float levitationPowerFactor = 28.0f;	// speed of object movement to destination position
float levitationMaxVelocity = 10.0f;	// max speed of object

float snatchSize = 1.8f;		// distance of "player hands"

// this method call in flush() method
void updateGravityGun()
{
if (mouseSelectedObject != NULL )
{
	// gravity forces calculations
	BodyRigid body = mouseSelectedObject.getBodyRigid();
	vec3 pos = mouseSelectedObject.getPosition();

	vec3 p0 = Math::decomposePositionXYZ(actor.getIModelview()); // take position of PlayerActor's camera
	vec3 p1 = actor.getViewDirection();

	vec3 p2 = p0 + p1 * snatchSize;	// calculate destination position (position of players hands)

	vec3 force = p2 - pos;		// target movement direction
	force *= levitationPowerFactor;	// movement coefficient

	// speed correction to max possible
	if (length(force) > levitationMaxVelocity)
		force = normalize(force) * levitationMaxVelocity;

	body.setLinearVelocity(force); // apply force
}
}

namespace Math
{
vec3 decomposePositionXYZ(mat4 transform)
{
	return transform.m03m13m23;
}
}

 

This section work fine, and looks like in many other games.

Link to comment

This realization have one omission: when player turns around our selected object don't change it rotation (see screenshot):

post-151-0-95293200-1300271000_thumb.jpg

It's not correct: we must see object in one angle, so we must take into account angular forces:

void updateGravityGun()
{
if (mouseSelectedObject != NULL )
{
	// gravity forces calculations
	. . .

	// angular forces calculations
	float angleVert = actor.getThetaAngle(); // vertical rotation of player's camera 
	float angleHorz = actor.getPhiAngle(); // horizontal rotation of player's camera 

	quat yRotation = quat(0, 1, 0, -angleVert); // create rotation quaternion for y axis (player's camera rotation)
	quat zRotation = quat(0, 0, 1, angleHorz); // create rotation quaternion for z axis (player's camera rotation)

	quat playerRotation = yRotation * zRotation; // rotation quaternion of player's camera

	quat currRotation = mouseSelectedObject.getRotation(); // rotation quaternion of selected object
	quat needRotation = -levitationRotation * playerRotation; // rotation quaternion, determine necessary rotation of selected object. levitationRotation - we save object rotation, when we try to select it, and add to it rotation of player's camera
	quat difRotation = currRotation - needRotation; // difference of current rotation and necessary orientation

	vec3 angularForce;
	float angularAngle;

	Math::toAxisAngle(difRotation, angularForce, angularAngle); // get axis vector and angle from quaternion 


	angularForce *= angularAngle; // rotation coefficient

	body.setAngularVelocity(-angularForce); // apply force

namespace Math
{
void toAxisAngle(quat q1, vec3 &axis, float &angle)
{
	// if w > 1 acos and sqrt will produce errors, this cant happen if quaternion is normalised
	if (q1.w > 1)
		q1 = quaternionNormalize(q1); 

	angle = 2 * acos(q1.w) * RAD2DEG;

	double s = sqrt(1 - q1.w * q1.w); // assuming quaternion normalised then w is less than 1, so term always positive.

	if (s < 0.001)//EPSILON))
	{
		// test to avoid divide by zero, s is always positive due to sqrt
		// if s close to zero then direction of axis not important
		axis.x = q1.x; // if it is important that axis is normalised then replace with x=1; y=z=0;
		axis.y = q1.y;
		axis.z = q1.z;
	}
	else
	{
		axis.x = q1.x / s; // normalise axis
		axis.y = q1.y / s;
		axis.z = q1.z / s;
	}
}
}
}

 

Adding angular forces in this manner add strange behavior: for some angles all good, for some - not:

-150 — 116 — object oriented on camera, correct

< -150 — object «convulsive» rotates in all axis

> 116 – object rotates in Z axis to left (relatively player)

 

I'm thinking I not correct get vector and angle from quaternion. Could you offer other patterns to determine difference with current and needed rotation of object or any thinks about?

Link to comment

Try more simple approach: JointFixed between physical body and player body.

 

I use this function to get axis angle inside the engine:

 

void quat::get(vec3 &axis,float &angle) const {
float ilength = Math::rsqrtf(x * x + y * y + z * z);
axis.x = x * ilength;
axis.y = y * ilength;
axis.z = z * ilength;
angle = Math::acosf(clamp(w,-1.0f,1.0f)) * RAD2DEG * 2.0f;
if(angle > 180.0f) angle -= 360.0f;
}

Link to comment

I use this function to get axis angle inside the engine:

 

void quat::get(vec3 &axis,float &angle) const {
float ilength = Math::rsqrtf(x * x + y * y + z * z);
axis.x = x * ilength;
axis.y = y * ilength;
axis.z = z * ilength;
angle = Math::acosf(clamp(w,-1.0f,1.0f)) * RAD2DEG * 2.0f;
if(angle > 180.0f) angle -= 360.0f;
}

 

This code can be accessed through Unigine script? I can't do this and add it to my Math namespace. Thanks.

 

Next problem, that we have, is different result on different FPS-level (although we use flush() method to calculate forces). Do we need use in flush() method some coefficient, based on game.getIFps() or something else?

Link to comment

I have some additional:

I change my quaternion to axis and angle function to frustum's void quat::get(vec3 &axis,float &angle). My function works incorrect. But this changes not gives me correct result. So I decide, that I have errors in quaternion difference determination, so I replace:

quat currRotation = mouseSelectedObject.getRotation(); // rotation quaternion of selected object
quat needRotation = -levitationRotation * playerRotation; // rotation quaternion, determine necessary rotation of selected object. levitationRotation - we save object rotation, when we try to select it, and add to it rotation of player's camera
quat difRotation = currRotation - needRotation; // difference of current rotation and necessary orientation

on next code:

quat currRotation = mouseSelectedObject.getRotation() * playerRotation;
quat needRotation = levitationRotation * playerRotation;

quat inverse = Math::quaternionInverse(currRotation); // find inverse quaternion, I do this to calculate difference between rotation quaternions for current object rotation and rotation, that he need have
quat difRotation = inverse * needRotation; // I read in articles about quaternions that we can calculate difference between rotation quaternions like inverse first quaternion multiplied on second quaternion

vec3 angularForce;
float angularAngle;

Math::getQuat(difRotation, angularForce, angularAngle); // this difference I can convert to axis and angle

body.setAngularVelocity(-angularForce); // and apply it to object

 

This code works no correct too, the force for angular velocity too small in this realization, but if I multiply force, then object rotate very quick... How do use think, this way is correct to do this? Thanks.

Link to comment

This code works no correct too, the force for angular velocity too small in this realization, but if I multiply force, then object rotate very quick... How do use think, this way is correct to do this? Thanks.

Answer by myself: this is working pattern, I had some errors when I get rotation difference quaternion.

To work with quaternions I use next functions:

#ifndef __MATH_H__
#define __MATH_H__

namespace Source
{

namespace Math
{
	quat quaternionConjugate(quat qa)
	{
		quat result;

		result.w =  qa.w;
		result.x = -qa.x;
		result.y = -qa.y;
		result.z = -qa.z;

		return result;
	}

	quat quaternionInverse(quat qa)
	{
		quat result = quaternionNormalize(qa);

		return quaternionConjugate(result);
	}

	float quaternionMagnitude(quat qa)
	{
		return sqrt(
				qa.w * qa.w +
				qa.x * qa.x +
				qa.y * qa.y + 
				qa.z * qa.z
				);
	}

	quat quaternionNormalize(quat qa)
	{
		return qa / quaternionMagnitude(qa);
	}

	void getQuat(quat q, vec3 &axis,float &angle) {
		float ilength = sqrt(q.x * q.x + q.y * q.y + q.z * q.z);
		axis.x = q.x * ilength;
		axis.y = q.y * ilength;
		axis.z = q.z * ilength;
		angle = acos(clamp(q.w,-1.0f,1.0f)) * RAD2DEG * 2.0f;

		if(angle > 180.0f) angle -= 360.0f;
	}
};
};

#endif

Frustum, maybe you add getQuat and quaternionInverse functions support in Unigine script?

Link to comment

Frustum, maybe you add getQuat and quaternionInverse functions support in Unigine script?

 

quaternionInverse() function is already existed as inverse(quat).

You can use dot(quat,quat) as quaternionMagnitude() function.

normalize(quat) function is also presented.

 

getQuat() is very specific function, I will add it later into the core/script library.

 

Why you don't use JointFixed for GravityGun?

Link to comment

quaternionInverse() function is already existed as inverse(quat).

Oh, I see... in some reason I thought this function does not work with quaternions :blink: I will use it

 

Why you don't use JointFixed for GravityGun?

We try use JointFixed, but we can't tune it so like we want, and we had problem: joint give opposite impulse to player, so when we lean on joint object player can fly on it :)

Link to comment
×
×
  • Create New...