Forum rules
The Projects forums are only for projects. If you are asking questions about a project, either find that project's thread, or start a thread in the General section instead.
I've got some objects (actors) in my mod that the player can use or bump into. I've got them represented by models instead of billboard sprites, so makes sense that the player should only be able to use them from the front of the model since that's where the switches and interactive parts are drawn on the models. There's nothing built-in to let an object only be triggered from a certain way so I wrote my own. This could be useful if you want a similar feature.
It works by checking the front plane of the hitbox against rays intersecting it. The front plane is projected according to the actor's angle, and is not axis-aligned ike the actual hitbox, so this routine is 360-degrees accurate. A limitation is that I don't consider z, Since Doom's "use" doesnt consider z, I didn't bother with it either.
Could also use this for example if you have a wheeled cart and the player can move it, but only from the front or back. Or create one of those annoying puzzles where they have to move blocks out of their way but the blocks only move in certain directions.
This could also be useful for example if you want to create a monster that can only be hit from the front, or negate that and it can only be hit from not the front. Or offset the detection angle and make a monster that is only hittable from the back. Or one that takes double damage from the back. You could also use this to detect if a projectile is a glancing blow instead of a direct hit and make glancing hits bounce off. But that last one you could probably do that with simpler math that I've got here.
class FaceSensitiveObject : actor
{
default
{
height 32;
radius 16;
+solid;
+special;
}
override void Touch(Actor toucher)
{
if (IsMovingToMyFront(toucher)) console.printf(level.time..": "..GetCharacterName()..": front touched by "..toucher.GetCharacterName());
else if (IsMovingToMyBack(toucher)) console.printf(level.time..": "..GetCharacterName()..": back touched by "..toucher.GetCharacterName());
}
override bool Used(Actor user)
{
if (IsLookingAtMyFront(user)) console.printf(level.time..": "..GetCharacterName()..": front used by "..user.GetCharacterName());
else if (IsLookingAtMyBack(user)) console.printf(level.time..": "..GetCharacterName()..": back used by "..user.GetCharacterName());
return true;
}
double sign(double val) //does ZScript not have this function built-in somewhere?
{
if (val > 0) return 1;
if (val < 0) return -1;
return 0;
}
//give me a ray's origin and angle and I will tell you if it intersects this actor's front face, or use offsetAngle to check another face
bool RayIntersectsMyFront(vector2 RayOrigin, double RayAngle, double offsetAngle = 0)
{
vector2 triggerCenter = Vec2Angle(radius, angle + offsetAngle);//project the trigger plane out by the radius
double radiusLong=sqrt(2. * radius * radius);//the "long" radius, center to corner
vector2 triggerCorner1 = Vec2Angle(radiusLong, angle + offsetAngle + 45);//the first edge of the trigger plane
vector2 triggerCorner2 = Vec2Angle(radiusLong, angle + offsetAngle - 45);//the other edge of the trigger plane
double angleTriggerToTripper = VectorAngle(RayOrigin.x - triggerCenter.x, RayOrigin.y - triggerCenter.y);
double relAngle = Normalize180(angleTriggerToTripper - angle - offsetAngle);//relative angle to the hit plane
if (-90 < relAngle && relAngle < 90) //check that the ray hits the front of the plane
{
double theta1=VectorAngle(triggerCorner1.x-RayOrigin.x, triggerCorner1.y-RayOrigin.y);
double theta2=VectorAngle(triggerCorner2.x-RayOrigin.x, triggerCorner2.y-RayOrigin.y);
double dtheta=theta2-theta1;
double ntheta=RayAngle-theta1;
dtheta=VectorAngle(cos(dtheta), sin(dtheta));
ntheta=VectorAngle(cos(ntheta), sin(ntheta));
return (sign(ntheta)==sign(dtheta)&&abs(ntheta)<=abs(dtheta));//check that the ray hits the plane at all
}
return false;
}
bool IsLookingAtMyFront(actor someone)
{
if (!someone) return false;
return RayIntersectsMyFront(someone.pos.xy, someone.angle);
}
bool IsLookingAtMyBack(actor someone)
{
if (!someone) return false;
return RayIntersectsMyFront(someone.pos.xy, someone.angle, 180);
}
bool IsMovingToMyFront(actor someone)
{
if (!someone) return false;
return RayIntersectsMyFront(someone.pos.xy, VectorAngle(someone.vel.x, someone.vel.y));
}
bool IsMovingToMyBack(actor someone)
{
if (!someone) return false;
return RayIntersectsMyFront(someone.pos.xy, VectorAngle(someone.vel.x, someone.vel.y), 180);
}
}
To help see how the code is working I'd suggest attaching a perfect cube model to the actor, the same size as the hitbox, or just use the radius debugger to do that automatically