I'm working on creating a voxel engine in LWJGL 3, I have all the basics down (chunks, mesh rendering, etc).
Now I'm working on adding physics using JBullet. This is my first time using JBullet directly, but I've used Bullet before in other 3D engines.
From here I gathered that all I needed to do to create a collision object the same shape as my mesh was the plug the vertices and indices into a TriangleIndexVertexArray and use that for a BvhTriangleMeshShape.
Here is my code:
float[] coords = mesh.getVertices();
int[] indices = mesh.getIndices();
if (indices.length > 0) {
IndexedMesh indexedMesh = new IndexedMesh();
indexedMesh.numTriangles = indices.length / 3;
indexedMesh.triangleIndexBase = ByteBuffer.allocateDirect(indices.length*Float.BYTES).order(ByteOrder.nativeOrder());
indexedMesh.triangleIndexBase.asIntBuffer().put(indices);
indexedMesh.triangleIndexStride = 3 * Float.BYTES;
indexedMesh.numVertices = coords.length / 3;
indexedMesh.vertexBase = ByteBuffer.allocateDirect(coords.length*Float.BYTES).order(ByteOrder.nativeOrder());
indexedMesh.vertexBase.asFloatBuffer().put(coords);
indexedMesh.vertexStride = 3 * Float.BYTES;
TriangleIndexVertexArray vertArray = new TriangleIndexVertexArray();
vertArray.addIndexedMesh(indexedMesh);
boolean useQuantizedAabbCompression = false;
BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(vertArray, useQuantizedAabbCompression);
CollisionShape collisionShape = meshShape;
CollisionObject colObject = new CollisionObject();
colObject.setCollisionShape(collisionShape);
colObject.setWorldTransform(new Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(position.x, position.y, position.z), 1f)));
dynamicsWorld.addCollisionObject(colObject);
} else {
System.err.println("Failed to extract geometry from model. ");
}
I know that the vertices and indices are valid as I'm getting them here after drawing my mesh.
This seems to somewhat work, but when I try to drop a cube rigidbody onto the terrain, it seems to collide way above the terrain! (I know that the cube is setup correctly because if I remove the mesh collider it hits the base ground plane at y=0).
I thought maybe it was a scaling issue (although I don't see how that could be), so I tried changing:
colObject.setWorldTransform(new Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(position.x, position.y, position.z), 1f))); to:
colObject.setWorldTransform(new Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(position.x, position.y, position.z), 0.5f)));
But after changing the scale from 1 it acted like the mesh collider didn't exist.
It's hard to find any resources or code for JBullet surrounding mesh collision, and I've been working on this for almost 2 days, so I'm hoping maybe some of you people who have done it before can help me out :)
Update 1:
I created an implementation of the IDebugDrawer so I could draw the debug infomation in the scene.
To test it I ran it with just a basic ground plane and a falling cube. I noticed that when the cube is falling the aabb matches the cube size, but when it hits the floor the aabb becomes significantly larger then it was.
I'm going to assue that this is normal Bullet behavior due to collition bouncing, and look at that later as it doesn't effect my current problem.
I re-enabled the generation of the colliders from the chunk meshs, and saw this:
It appears that the aabb visualization of the chunk is a lot higher then the actual chunk (I know my y positioning of the overall collision object is correct).
I'm going to try to figure out if I can draw the actual collision mesh or not.
Update 2:
As far as I can see looking at the source, the meshof the colliders should be drawing in debug, so I'm not sure why it isn't.
I tried changing the Box rigidbody to a sphere, and it actually rolled across the top of the visualized aabb for the terrain collider. It just rolled flat though and didn't go hit or down where there where hills or dips in the terrain where, so it was obviously just rolling across the flat top of the aabb.
So after adding in the Debug Drawer, I was confused as to why the aabb was x2 larger then it should have been.
After spending hours trying little adjustments, I noticed something odd - there was a 0.25 gap between the collider and the edge of the chunk. I proceeded to zoom out and surprisingly noticed this:
There is an extera row and column of colliders? No that doesn't make sense, there should be 5x5 colliders to match the 5x5 chunks.
Then I counted blocks and realized that the colliders where spanning 64 blocks (my chunks are 32x32!).
I quickly realized that this was a scaling issue, and after adding
BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(vertArray, useQuantizedAabbCompression);
meshShape.setLocalScaling(new Vector3f(0.5f, 0.5f, 0.5f));
To scale the colliders down by half, everything fit and worked! My "sphere" rolled and came to a stop where there was a hill in the terrain like it should.
My full code for coverting an LWJGL mesh to a JBullet mesh collder is:
public void addMesh(org.joml.Vector3f position, Mesh mesh){
float[] coords = mesh.getVertices();
int[] indices = mesh.getIndices();
if (indices.length > 0) {
IndexedMesh indexedMesh = new IndexedMesh();
indexedMesh.numTriangles = indices.length / 3;
indexedMesh.triangleIndexBase = ByteBuffer.allocateDirect(indices.length*Integer.BYTES).order(ByteOrder.nativeOrder());
indexedMesh.triangleIndexBase.rewind();
indexedMesh.triangleIndexBase.asIntBuffer().put(indices);
indexedMesh.triangleIndexStride = 3 * Integer.BYTES;
indexedMesh.numVertices = coords.length / 3;
indexedMesh.vertexBase = ByteBuffer.allocateDirect(coords.length*Float.BYTES).order(ByteOrder.nativeOrder());
indexedMesh.vertexBase.rewind();
indexedMesh.vertexBase.asFloatBuffer().put(coords);
indexedMesh.vertexStride = 3 * Float.BYTES;
TriangleIndexVertexArray vertArray = new TriangleIndexVertexArray();
vertArray.addIndexedMesh(indexedMesh);
boolean useQuantizedAabbCompression = false;
BvhTriangleMeshShape meshShape = new BvhTriangleMeshShape(vertArray, useQuantizedAabbCompression);
meshShape.setLocalScaling(new Vector3f(0.5f, 0.5f, 0.5f));
CollisionShape collisionShape = meshShape;
CollisionObject colObject = new CollisionObject();
colObject.setCollisionShape(collisionShape);
colObject.setWorldTransform(new Transform(new Matrix4f(new Quat4f(0, 0, 0, 1), new Vector3f(position.x, position.y, position.z), 1f)));
dynamicsWorld.addCollisionObject(colObject);
} else {
System.err.println("Failed to extract geometry from model. ");
}
}
Update 1:
Even though the scaling was the fix for said prolem, it caused me to look deeper and realize that I mistakenly was using to block size (0.5f) for the mesh scaling factor in my mesh view matrix. Changing the scale to 1 like it should be fixed it.
Related
I am using LibGDX and have an ArrayList of multiple points which I want to connect. I am aware there are several methods with ShapeRenderer that work, however im running a SpriteBatch at the same time so what do I do now to draw a line with two Vectors. (If it exists, I'd also like the function that draws multiple lines at once with an Àrray or Vector2 as a parameter, though it isn't a problem as otherwise I'd manage with a for-loop probably).
I am also aware I can use Pixmaps but they don't seem to work correctly. Here is my attempt:
// point1 and point2 are of type Vector2
Pixmap pixmap = new Pixmap(point2.x - point1.x, 2, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.WHITE);
pixmap.drawLine(point1.x, point1.y, point2.x, point2.y);
In response to a possible solution that involves using ShapeRenderer at the same time, this problem arises (the second image uses the points with pixmaps, the first the ShapeRenderer with lines)
The code used for the first image is the following:
for(int i = 1; i < dotPositions.size(); i++) {
sr.line(dotPositions.get(i-1), dotPositions.get(i));
}
The code used for the second image is the following:
Pixmap pixmap = new Pixmap(2, 2, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.WHITE);
pixmap.fillCircle(2, 2, 2);
Texture texture = new Texture(pixmap);
for(int i = 1; i < dotPositions.size(); i++) {
batch.draw(texture, dotPositions.get(i).x, dotPositions.get(i).y);
}
In both cases dotPositions is an ArrayList<Vector2> with the same values.
If anyone in the future may be interested, I found the solution and all I had to do was use ShapeRender.setProjectionMatrix(cam.combined) to sync it with the SpriteBatch.
Good night friends.
I'm having trouble drawing a fixed point on the screen when the screen is rotated. I used the method "rotateAround" from the position of the player.
It seems to me. I have to rotate this fixed point also from the position of the player. I use this stretch learned here in stackoverflow.
public void rotate(Vector3 position, Vector3 centerPoint){
this.cosTemp = MathUtils.cosDeg(this.anguloAtual);
this.senTemp = MathUtils.sinDeg(this.anguloAtual);
this.xTemp = centerPoint.x + ((position.x - centerPoint.x) * this.cosTemp) - ((position.y - centerPoint.y) * this.senTemp);
this.yTemp = centerPoint.y + ((position.y - centerPoint.y) * this.cosTemp) + ((position.x - centerPoint.x) * this.senTemp);
position.set(this.xTemp, this.yTemp, 0);
}
In the drawing that the player on the screen. I used the position of the player, then called "camera.project" then the method "rotate". The fixed point appears, however it is not exactly fixed.
I used the example of a fixed point slightly ahead of the player.
public void meDesenhar(SpriteBatch spriteBatch) {
spriteBatch.begin();
this.spritePlayer.setPosition(this.positionPlayer.x - (this.spritePlayer.getWidth() / 2),
this.positionPlayer.y - this.spritePlayer.getHeight() / 2);
this.spritePlayer.draw(spriteBatch);
spriteBatch.end();
originPosition.set(positionPlayer, 0);
fixedPosition.set(positionPlayer.x, positionPlayer.y + 10, 0);
cameraTemp.project(fixedPosition);
cameraTemp.project(originPosition);
cameraManagerTemp.rotate(fixedPosition, originPosition);
Debugagem.drawPointInScreen(Color.BLUE, fixedPosition);
}
My questions:
1 - I am doing something wrong, or just it is a result of rounding? I realized when debugging. The position of the player changed a little every rotation after the "camera.project". Example position (540, 320) turned (539.99, 320.013)
2 - I tried using and enjoying the SpriteBatch the draw method to perform the rotation however, could not make the rotation from the player. I would arrive at the same result.
3 - Can I use two cameras? Each camera would be a layer. A camera at the map and the player would be. The other for fixed point. It's viable? I could not find any example that works with more than one camera at the same time. Anyone know any examples please. I'm not talking about huds or cameras to stage.
Video follows.
https://www.youtube.com/watch?v=1Vg8haN5ULE
Thank you.
It can be result of rounding because its moving a pixel.
You can calculate rotation from the player but its not necessary.
Of course you can use multiple cameras in your game and you should also in this case.
Its few screenshot from my old projects that i used multiple cameras
As you can see you can even use different type of cameras like ortho and perspective both 2D and 3D.
Just create new camera like first one and change projection matrix
camrotate = new OrthographicCamera(540, 960);
//...
camfixed = new OrthographicCamera(540, 960);
//...
And in render method
batch.setProjectionMatrix(camrotate.combined);
batch.begin();
//draw in camrotate now
//...
//...
batch.end();
batch.setProjectionMatrix(camfixed.combined);
batch.begin();
//draw fixed elements now
//...
//...
batch.end();
//add one more camera if you need
Edit:
Change projection matrix outside of batch.begin()/end() otherwise the current batch will flushed.
When trying to program a game using Box2D, I ran into a problem with Box2D. I filled in pixel numbers for the lengths of the the textures and sprites to create a box around it. Everything was at the right place, but for some reason everything went very slowly. By looking on the internet I found out that if you didn't convert pixels to meters box2d might handle shapes as very large objects. this seemed to be a logical cause of everything moving slowly.
I found similar questions on this site, but the answers didn't really seem to help out. in most of the cases the solution was to make methods to convert the pixel numbers to meters using a scaling factor. I tried this out, but everything got misplaced and had wrong sizes. this seemed logical to me since the numbers where changed but had the same meaning.
I was wondering if there is a way to make the pixels mean less meters, so everything whould be at the same place with the same (pixel) size, but mean less meters.
If you have a different way which you think might help, I whould also like to hear it..
Here is the code i use to create the camera
width = Gdx.graphics.getWidth() / 5;
height = Gdx.graphics.getHeight() / 5;
camera = new OrthographicCamera(width, height);
camera.setToOrtho(false, 1628, 440);
camera.update();
This is the method I use to create an object:
public void Create(float X, float Y, float Width, float Height, float density, float friction, float restitution, World world){
//Method to create an item
width = Width;
height = Height;
polygonDef = new BodyDef();
polygonDef.type = BodyType.DynamicBody;
polygonDef.position.set(X + (Width / 2f), Y + (Height / 2f));
polygonBody = world.createBody(polygonDef);
polygonShape = new PolygonShape();
polygonShape.setAsBox(Width / 2f, Height / 2f);
polygonFixture = new FixtureDef();
polygonFixture.shape = polygonShape;
polygonFixture.density = density;
polygonFixture.friction = friction;
polygonFixture.restitution = restitution;
polygonBody.createFixture(polygonFixture);
}
To create an item, in this case a table, I use the following:
Table = new Item();
Table.Create(372f, 60f, 152f, 96f, 1.0f, 0.2f, 0.2f, world);
The Sprites are drawn on the item by using the following method:
public void drawSprite(Sprite sprite){
polygonBody.setUserData(sprite);
Utils.batch.begin();
if(polygonBody.getUserData() instanceof Sprite){
Sprite Sprite = (Sprite) polygonBody.getUserData();
Sprite.setPosition(polygonBody.getPosition().x - Sprite.getWidth() / 2, polygonBody.getPosition().y - Sprite.getHeight() / 2);
Sprite.setRotation(polygonBody.getAngle() * MathUtils.radiansToDegrees);
Sprite.draw(Utils.batch);
}
Utils.batch.end();
}
The sprites also have pixel sizes.
Using this methods it displays the images at the right places, but everything moves slowly.
I was wondering how or if I whould have to change this to make the objects move correctly, and / or mean less. Thanks in advance.
Box2D is an entirely independent of the graphics library that you use. It doesn't have any notion of sprites and textures. What you read online is correct, you'll have to convert pixels to metres, as Box2D works with metres(the standard unit for distance).
For example, if you drew a sprite of size 100x100 pixels, that's the size of the sprite that you want the user to see on the screen. In real world the size of the object should be in metres and not in pixels - so if you say 1px = 1m, then that'll map the sprite to a gigantic 100x100 meter object. In Box2D, large world objects will slow down calculations. So what you need to do is map the 100 pixels to a smaller number of meters, say, 1 meter - thus 100x100px sprite will be represented in Box2D world by a 1x1 meter object.
Box2D doesn't work well with very small numbers and very large numbers. So keep it in between, say between 0.5 and 100, to have good performance.
EDIT:
Ok. Now I get your question.
Don't code to pixels. Its as simple as that. I know it'll take some time to understand this(it took for me). But once you get the hang of it, its straight forward.
Instead of pixels, use a unit, say, you call it meter.
So we decide our viewport should be say 6mx5m.
So initialization is
Constants.VIEWPORT_WIDTH = 6;
Constants.VIEWPORT_HEIGHT = 5;
...
void init() {
camera = new OrthographicCamera(Constants.VIEWPORT_WIDTH, Constants.VIEWPORT_HEIGHT);
camera.position.set(Constants.VIEWPORT_WIDTH/2, Constants.VIEWPORT_HEIGHT/2, 0);
camera.update();
}
Once you know the actual width and height, you call the following function in order to maintain aspect ratio:
public void resize(int width, int height) {
camera.viewportHeight = (Constants.VIEWPORT_WIDTH / width) * height;
camera.update();
}
resize() can be called anytime you change your screen size(eg: when you screen orientation changes). resize() takes the actual width and height (320x480 etc), which is the pixel value.
Now you specify you sprite sizes, their positions etc. in this new world of size 6x5. You can forget pixels. The minimum size of the sprite that'll fill the screen will be 6x5.
You can now use the same unit with Box2D. Since the new dimensions will be smaller, it won't be a problem for Box2D. If I remember correctly Box2D doesn't have any unit. We just call it meter for convenience sake.
Now you might ask where you specify the dimensions of the window. It depends on the platform. Following code shows a 320x480 windowed desktop game:
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "my-game";
cfg.useGL20 = false;
cfg.width = 480;
cfg.height = 320;
new LwjglApplication(new MyGame(), cfg);
}
}
Our camera will intelligently map the 6x5 viewport to 480x320.
I'm trying to create a camera to move around a 3d space and am having some problems setting it up. I'm doing this is Java, and apparently using gluPerspective and gluLookAt together creates a conflict (the screen starts flickering like mad).
gluPerspective is set like this:
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(50.0f, h, 1.0, 1000.0);
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
I then create a camera matrix, making use of eye coordinates, forward and up vectors (http://people.freedesktop.org/~idr/glu3/form_4.png) (lets assume the code for the camera is correct.
Lastly, before I draw any thing I have:
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
gl.glLoadIdentity();
gl.glMultMatrixf(camera.matrix);
And then I call my drawing routines (which do some translation/rotation on their own by calling glRotatef and glTranslatef).
Without the call to glMultMatrixf the camera shows the items I need to see in the centre of the screen as it should. With glMulMatrixf however, all I get is a black screen. I tried using glLoadMatrixf instead and it didn't work either. Am I doing something wrong? Am I putting something out of place? If not, and this is how it should be done let me know and I'll post some of the camera code that might be creating the conflicts.
EDIT: Here is the camera matrix creation code:
private void createMatrix()
{
float[] f = new float[3]; //forward (centre-eye)
float[] s = new float[3]; //side (f x up)
float[] u = new float[3]; //'new up' (s x f)
for(int i=0;i<3;i++){
f[i] = centre[i]-eye[i];
}
f = Maths.normalize(f);
s = Maths.crossProduct(f,upVec);
u = Maths.crossProduct(s,f);
float[][] mtx = new float[4][4];
float[][] mtx2 = new float[4][4];
//initializing matrices to all 0s
for (int i = 0; i < mtx.length; i++) {
for (int j = 0; j < mtx[0].length; j++) {
mtx[i][j] = 0;
mtx2[i][j] = 0;
}
}
//mtx = [ [s] 0,[u] 0,[-f] 0, 0 0 0 1]
//mtx2 = [1 0 0 -eye(x), 0 1 0 -eye(y), 0 0 1 -eye(z), 0 0 0 1]
for(int i=0;i<3;i++){
mtx[0][i] = s[i];
mtx[1][i] = u[i];
mtx[2][i] = -f[i];
mtx2[i][3]=-eye[i];
mtx2[i][3]=-eye[i];
mtx2[i][3]=-eye[i];
}
mtx[3][3] = 1;
mtx2[0][0]=1;mtx2[1][1] = 1;mtx2[2][2] = 1;mtx2[3][3] = 1;
mtx = Maths.matrixMultiply(mtx,mtx2);
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
// this.mtx is a float[16] for glMultMatrixf
this.mtx[i*4+j] = mtx[i][j];
}
}
}
I'm hopping the error is somewhere in this piece of code, if not, I'll have a look at my maths functions to see whats going on..
EDIT2: Though I should mention that at least the initial vectors (eye,centre,up) are correct and do put teh camera where it should be (worked with gluLookAt but had teh flickering issue).
It might be simpler to use glRotatef, glTranslatef, and glFrustum to create the camera, although your math seems fine to me (just as long as UpVec is actually defined). In most of the 3D graphics that I have done, you didn't really have a defined object that you wanted to track. I went through various implementations of a 3D camera using gluLookAt before I finally settled on this.
Here is how I tend to define my cameras:
When I create or initialize my camera, I set up the projection matrix with glFrustum. You can use glPerspecive if you prefer:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(left, right, down, up, near, far);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
After I clear the color and depth buffers for a render pass, then I call
glLoadIdentity();
glRotated(orientation.x, 1.0, 0.0, 0.0);
glRotated(orientation.y, 0.0, 1.0, 0.0);
glRotated(orientation.z, 0.0, 0.0, 1.0);
glTranslatef(position.x, position.y, position.z);
To position and orient the camera. Initially, you set position and orientation both to {0}, then add or subtract from position when a key is pressed, and add or subtract from orientation.x and orientation.y when the mouse is moved... (I generally don't mess with orientation.z)
Cheers.
Fixed it kind of. The problem was using glMultMatrix(float[] matrix,int ?ofset?)... for some reason if I just use glMultMatrix(FloatBuffer matrix) it works fine..
There are some issues with the transformations I'm making but I should be able to deal with those... Thank you for your input though guys.
I've been playing with some algorithms on the internet for a while and I can't seem to get them to work, so I'm tossing the question out here;
I am attempting to render a velocity vector line from a point. Drawing the line isn't difficult: just insert a line with length velocity.length into the graph. This puts the line centered at the point in the y-axis direction. We need to get this now in the proper rotation and translation.
The translational vector is not difficult to calculate: it is half the velocity vector. The rotational matrix, however, is being exceedingly elusive to me. Given a directional vector <x, y, z>, what's the matrix I need?
Edit 1: Look; if you don't understand the question, you probably won't be able to give me an answer.
Here is what I currently have:
Vector3f translation = new Vector3f();
translation.scale(1f/2f, body.velocity);
Vector3f vec_z = (Vector3f) body.velocity.clone();
vec_z.normalize();
Vector3f vec_y; // reference vector, will correct later
if (vec_z.x == 0 && vec_z.z == 0) {
vec_y = new Vector3f(-vec_z.y, 0f, 0f); // could be optimized
} else {
vec_y = new Vector3f(0f, 1f, 0f);
}
Vector3f vec_x = new Vector3f();
vec_x.cross(vec_y, vec_z);
vec_z.normalize();
vec_y.cross(vec_x, vec_z);
vec_y.normalize();
vec_y.negate();
Matrix3f rotation = new Matrix3f(
vec_z.z, vec_z.x, vec_z.y,
vec_x.z, vec_x.x, vec_x.y,
vec_y.z, vec_y.x, vec_y.y
);
arrowTransform3D.set(rotation, translation, 1f);
based off of this article. And yes, I've tried the standard rotation matrix (vec_x.x, vec_y.x, etc) and it didn't work. I've been rotating the columns and rows to see if there's any effect.
Edit 2:
Apologies about the rude wording of my comments.
So it looks like there were a combination of two errors; one of which House MD pointed out (really bad naming of variables: vec_z was actually vec_y, and so on), and the other was that I needed to invert the matrix before passing it off to the rendering engine (transposing was close!). So the modified code is:
Vector3f vec_y = (Vector3f) body.velocity.clone();
vec_y.normalize();
Vector3f vec_x; // reference vector, will correct later
if (vec_y.x == 0 && vec_y.z == 0) {
vec_x = new Vector3f(-vec_y.y, 0f, 0f); // could be optimized
} else {
vec_x = new Vector3f(0f, 1f, 0f);
}
Vector3f vec_z = new Vector3f();
vec_z.cross(vec_x, vec_y);
vec_z.normalize();
vec_x.cross(vec_z, vec_y);
vec_x.normalize();
vec_x.negate();
Matrix3f rotation = new Matrix3f(
vec_x.x, vec_x.y, vec_x.z,
vec_y.x, vec_y.y, vec_y.z,
vec_z.x, vec_z.y, vec_z.z
);
rotation.invert();
This should do you
Dupe.
The question there involves getting a rotation to a certain axis, whereas I'm concerned with getting a rotation matrix.
Gee, I wonder if you could turn convert one to the other?
BTW, your current solution of picking an arbitrary y axis and then reorthogonalising should work fine; it looks bugged though, or at least badly written. 'z_vec' is not a good variable-name for the y-axis. What's with the 'z,x,y' ordering, anyway?
If it still doesn't work, try making random changes until it does - transpose the matrix, negate vectors until you have an even number of sign errors, that kind of thing.
Also your tone of voice comes across as sort-of rude, given that you're asking strangers to spend their time helping you.