Heh
#851 posted by necros on 2012/08/10 01:18:40
actually, flipping x axis and reversing the order is basically just flipping y axis right? :P
Triple Post
#852 posted by necros on 2012/08/10 01:38:41
looks like it's a thing with obj... just flipped v component when loading the obj instead of goofing with the pcx.
Y Axis
#853 posted by Preach on 2012/08/10 10:15:43
There is an issue to be aware of with "intuitive" coordinates and storage of data. The natural order of storing skin/image data (at least the one taken in the mdl format) comes about I think from their usual representation as a string (in the purest array of numbers sense). Since we are used to text flowing from left-to-right then step down a line and repeat (for english-speakers) we extend that idea to the order pixels should be stored in the skin.
The place where this begins to cause a conflict is when we start thinking about cartesian co-ordinates on the skin. The standard mathematical axes on a graph has the y value increase as you move upwards. So if you have something that wants to put the origin of the coordinates in the bottom-left of the skin, you need to reflect the skin in y to compensate.
Having said that, if you're actually going to render the 2d skin somewhere, you really ought to apply the reflection to the skin vertices rather than the image file itself. Otherwise you will cause a lot of confusion for the user who has an expectation on what their skin looked like.
#854 posted by necros on 2012/08/10 19:40:18
Having said that, if you're actually going to render the 2d skin somewhere, you really ought to apply the reflection to the skin vertices rather than the image file itself. Otherwise you will cause a lot of confusion for the user who has an expectation on what their skin looked like.
Yes, this is what I ended up doing. I want to be able to display the skin either for drawing on (probably not) or changing UV coordinates, so having the thing upside down is not an option.
Turns out that this is a common thing with the OBJ format as many of the loading examples I read had a bit of code involving flipping the v component of the texture coordinates.
Woohoo
#855 posted by necros on 2012/08/11 06:27:00
OBJ -> MDL perfectly functional! FUCK YEAH.
Still can't import more than 1 frame, but I don't think that's going to be a big deal.
Coding Challenge
#856 posted by Preach on 2012/08/11 12:39:05
Suppose that we have the following functions:
is_first_player: returns true if self is the first player in the server
is_last_player: returns true if self is the last player in the server
toggle_func_playerclips: finds all func_playerclip entities in the map and toggles them between SOLID_BSP and SOLID_NOT.
Your task: comment and critique the following scheme for implementing func_playerclip entities:
1) add
if(is_first_player())
toggle_func_playerclips();
to PlayerPreThink.
2) add
if(is_last_player())
toggle_func_playerclips();
to PlayerPostThink.
The main question for consideration: is there any way for a player to become 'stuck'? Extra credit for considering the implications if is_first_player and is_last_player are not available.
Coding Challenge Fixed
#857 posted by Preach on 2012/08/11 12:40:22
Forgot to preview so the entities weren't encoded...
Your task: comment and critique the following scheme for implementing func_playerclip entities:
1) add
if(is_first_player())
����toggle_func_playerclips();
to PlayerPreThink.
2) add
if(is_last_player())
����toggle_func_playerclips();
to PlayerPostThink.
#858 posted by necros on 2012/08/11 21:35:38
You might not be able to change .solid like that without setting model.
Might be safer to do the func_togglewall trick where you translate the clipmodel far away when 'off' and put it back into place when 'on'.
Beyond that, at first glance, seems like it would work?
Re: Gb
#859 posted by Tronyn on 2012/08/11 23:51:12
Hey, thanks for the response - helpful stuff. What's your email / how can I contact you? PS all the stuff on your blog looks amazing. Cheers.
Clipped Out
#860 posted by Preach on 2012/08/12 04:01:49
You might not be able to change .solid like that without setting model.
Might be safer to do the func_togglewall trick where you translate the clipmodel far away when 'off' and put it back into place when 'on'.
Yeah, I explained myself badly. The plan was to only describe abstract functions, so you could assume they were correct without getting bogged down. I leaked too many implementation details...
There's still one case I can think of where it breaks down though - the trick is to work out when the player moves other than within the player physics code. There's also a more philosophical quandary with pointcontents I meant to bring up in the first post.
I'm glad you mentioned togglewalls, because although they seem quite hackish with the far-away trick, it has some hidden advantages. The prime one is avoiding ever toggling solid status within a touch function - which can be a fatal error. Building a monsterclip with the same broad outline would now become feasible.
You do have to bind the toggling to calls to walkmove and movetogoal, because monsters don't have the simple physics-wrapping functions that players do. This means that the monsterclip is imperfect in a way that the playerclip is not: falling or jumping monsters will pass through monsterclip. Still, better than a kick in the teeth.
#861 posted by necros on 2012/08/12 04:09:27
You do have to bind the toggling to calls to walkmove and movetogoal, because monsters don't have the simple physics-wrapping functions that players do.
This is what I do right now. To avoid toggling every monster clip, I bind specific clips to specific monsters via a target->targetname style link, that way if a monster has no monsterclips associated with it, it doesn't toggle anything otherwise, it's only toggling a single monsterclip.
I suppose you could just make one giant monster clip entity for the entire map, but I'm not sure if that's bad having an entity with collision cover such a huge area so I've left it as localized clips.
#862 posted by necros on 2012/08/12 04:50:08
I think I got a little side-tracked...
There's still one case I can think of where it breaks down though - the trick is to work out when the player moves other than within the player physics code. There's also a more philosophical quandary with pointcontents I meant to bring up in the first post.
Why would it move outside of engine physics? Feeding .velocity in still has to wait for the engine to process it.
Unless you mean setorigin()? But I think it's safe to assume using setorigin is always going to break collision.
Hm.. well, unless you are doing some funny trickery where you are using a proxy to move the player...
#863 posted by Preach on 2012/08/12 11:51:22
But I think it's safe to assume using setorigin is always going to break collision.
Yeah, agreed.
Why would it move outside of engine physics?
Still inside engine physics, but outside of the player's physics pass.
#864 posted by necros on 2012/08/12 19:02:36
hm... Maybe when you're standing on a bmodel that's moving? ie: train or lift
#865 posted by Preach on 2012/08/12 19:23:06
Maybe when you're standing on a bmodel that's moving? ie: train or lift
Bingo. Probably not a big deal once you know about it, because usually those things are put in for the benefit of the player and you're unlikely to intentionally clip off their route, just something that users of the entity would have to be aware of.
#866 posted by necros on 2012/08/12 19:27:05
The whole player movement on other bmodels thing is kind of fuzzy for me. It's not true movement because the minute you jump, you loose the bmodel's velocity and just get your own personal velocity back.
It's too bad because it stops things like throwing the player off a fast moving object or launching them through the air from a catapult.
Jumping Monsters
#867 posted by sock on 2012/08/19 23:01:04
I want to calculate the height and speed to make a monster jump a certain distance. I know the origin of both the start and finish points but I don't know how to calculate the speed and velocity in QC.
I know there is 'trigger_monsterjump' but you have to specify or just guess the right amounts until it is right. is there a way I can do a quick QC formula to get rid of the guess work?
Pretty Easy
#868 posted by Preach on 2012/08/19 23:45:09
Start by picking a velocity upwards, call that v_z. Let d_z be the height of the end point minus the height of the start point.
Once your monster launches, the only thing affecting it is gravity, which accelerates at 800 units per second per second(*). We will appeal to one of the standard equations of motion (with constant acceleration)
s = ut + �at�
Where s is the distance travelled, u is the initial velocity, t is time and a is the acceleration. Substituting the values from above and rearranging:
400 t� - v_z * t + d_z = 0
with solution t = (v_z + sqrt(v_z� - 1600 d_z) )/800
On level ground this simplifies to:
t = v_z / 400
Now that we have the time t it takes to fall to the correct height, we need only divide the horizontal distance between the start and end points by t to get the required horizontal velocity. Sum the horizontal and vertical velocity vectors and you are done.
We seem to have complete freedom on how to choose v_z, but there are two things to think about. Firstly flight time is roughly proportional to v_z, so vary it according to how long you'd like the jump to last. Secondly in the case that d_z is positive (the end point is above the start point), there is a minimum requirement to v_z. The value inside the square root must be positive for the equation to work, so take care.
Please also remember that although we have solved the equations exactly, the simulation of the motion in the engine will not be exact, and in any case rounding errors will occur. Do not rely on any method to give you 100% accuracy - the landing point may even vary a small amount depending on framerate.
The really interesting case is the one where you have a fixed total velocity and still want to try and land on a particular point, but no space for that right now.
(*) 800 is the default, but in reality you should use the value of sv_gravity. It should be easy to replace the constant, but the explanation focuses on the important variables for clarity.
#869 posted by Spirit on 2012/08/19 23:47:54
check if some mods with "z-aware ogres" (one of the worst quake gameplay changes people like to call a bugfix) are open-source and allow copying, maybe you can use the calculation from their aim.
Confused
#870 posted by sock on 2012/08/20 00:56:41
Thanks Preach for the explanation but I still don't understand what to code in QC. I know values for some stuff but not the initial velocity. Also I don't mind this is not 100% accurate, I just want something that is roughly right to the nearest 64 units.
I know the following values:
start and finish origin
(s) distance = finish_z - start_z
(a) acceleration = 800 (will use sv variable)
(t) time = s / 200 (wild guess)
(I assume objects travel 200 units forward top speed, it would be nice to know what this value is for monsters)
Things I don't know:
(u) initial velocity = ??
formula:
s = ut + �at�
Expanded out:
s = u * t + sqr ( ( a * 0.5) * t )
I don't know what 'u' is, how do I re-arrange this to find initial velocity (u)?
@spirit, z aware monsters are not all bad, the problem is players don't expect the change and get angry because things are different for no apparent reason. The best solution for z aware monsters is to link them to a high skill level and make them visually different.
Arbitration
#871 posted by Preach on 2012/08/20 01:27:06
u is defined in the first paragraph:
Start by picking a velocity upwards, call that v_z.
The terminology is a bit mixed up, u is the standard name for the initial velocity in that equation; the name v_z was chosen earlier to suggest a vector component style but in retrospect confuses things.
The key is that it's arbitrary, you have complete freedom to pick whatever upwards velocity you like, then you calculate a forwards velocity to match it. I say complete freedom, but there is some guidance in the paragraph 4th from the bottom.
#872 posted by necros on 2012/08/20 01:48:48
It helps to break things down a bit to understand the problem.
So first is a very basic part of the whole problem, the horizontal speed.
If velocity is 600 units/sec, then that means, in 1 second, you have a distance covered of 600 units.
Seems dumb, but the next part is what you are trying to figure out:
If you KNOW the horizontal speed you want to move at, then what you need to do is find out how high to jump so that you stay in the air for that amount of time.
At this point, it can be easier to wing it.
First find the horizontal distance:
(self is some monster)
vector vec = self.origin - self.enemy.origin;
vector vec_z = 0; //this flatten the vector
float hdist = vlen(temp2 - temp1);
next we decide how fast we want the guy to jump:
float hspeed = 600; //fiends jump this fast horizontally
at this point, we could do something like this:
float vtime = hdist / hspeed;
vtime is now distance / speed (which is units of time)
So we have the amount of time needed to stay in the air.
vector dir = normalize(vec);
self.velocity = (vtime * 400) * '0 0 1' + (dir * hspeed);
the vtime * 400 part is the winging it bit. It works well for most values below super fast velocities.
#873 posted by necros on 2012/08/20 01:50:30
also, it might be a good idea to cap some values. depending on how far the monster is from it's target, vtime can potentially be quite long, meaning the velocity needed to stay in the air will be silly high.
either that or disallow the jump completely beyond a range.
#874 posted by necros on 2012/08/20 01:51:34
ohhh, finally, this is only accurate for cases where both monster and target are on equal height. for any other case, you will need to use the physics equation.
The Final Jump
#875 posted by sock on 2012/08/20 04:49:22
Thanks for the help everyone, here is my final chunk of code for my jump system. It works really well and all the jumps I have setup always go downwards so the gravity/4 looks more natural than using /2.
some constants I used:
MONAI_JUMPSPEED = 200;
DEF_GRAVITY = 800;
Code:
local vector jumporg, jumptarg, jumpdir;
local float jumpdist, jumptime;
// Calculate jump velocity towards destination node
jumporg = self.origin;
jumptarg = self.target_ainode.origin;
jumporg_z = jumptarg_z = 0; // Flatten vectors
// turn towards jump destination node
self.ideal_yaw = vectoyaw(jumptarg - jumporg);
self.angles_y = self.ideal_yaw;
// Calculate distance and time
jumpdist = vlen(jumptarg - jumporg);
jumptime = jumpdist / MONAI_JUMPSPEED;
jumpdir = normalize(jumptarg - jumporg);
self.velocity = (jumptime * (DEF_GRAVITY/4) ) * '0 0 1' + (jumpdir * MONAI_JUMPSPEED);
Hopefully it might be helpful to someone else.
|