/// <summary> /// Mover Elipsoide con detección de colisiones, sliding y gravedad. /// Se actualiza la posición del centro del Elipsoide /// </summary> /// <param name="characterElipsoid">Elipsoide del cuerpo a mover</param> /// <param name="movementVector">Movimiento a realizar</param> /// <param name="colliders">Obstáculos contra los cuales se puede colisionar</param> /// <returns>Desplazamiento relativo final efecutado al Elipsoide</returns> public Vector3 moveCharacter(TgcElipsoid characterElipsoid, Vector3 movementVector, List <Collider> colliders) { //Guardar posicion original del Elipsoide var originalElipsoidCenter = characterElipsoid.Center; //Pasar elipsoid space var eCenter = TgcVectorUtils.div(characterElipsoid.Center, characterElipsoid.Radius); var eMovementVector = TgcVectorUtils.div(movementVector, characterElipsoid.Radius); eSphere.setValues(eCenter, 1); var eOrigCenter = eSphere.Center; //Ver si la distancia a recorrer es para tener en cuenta var distanceToTravelSq = movementVector.LengthSq(); if (distanceToTravelSq >= EPSILON) { //Mover la distancia pedida selectPotentialColliders(characterElipsoid, movementVector, colliders); result = doCollideWithWorld(eSphere, eMovementVector, characterElipsoid.Radius, objetosCandidatos, 0, movementSphere, 1); } //Aplicar gravedad if (GravityEnabled) { //Mover con gravedad var eGravity = TgcVectorUtils.div(GravityForce, characterElipsoid.Radius); selectPotentialColliders(characterElipsoid, eGravity, colliders); result = doCollideWithWorld(eSphere, eGravity, characterElipsoid.Radius, objetosCandidatos, 0, movementSphere, OnGroundMinDotValue); } //Mover Elipsoid pasando valores de colision a R3 var movement = TgcVectorUtils.mul(eSphere.Center - eOrigCenter, characterElipsoid.Radius); characterElipsoid.moveCenter(movement); //Ajustar resultados result.realMovmentVector = TgcVectorUtils.mul(result.realMovmentVector, characterElipsoid.Radius); result.collisionPoint = TgcVectorUtils.mul(result.collisionPoint, characterElipsoid.Radius); return(movement); }
/// <summary> /// Selecciona todos los colliders que estan dentro de la esfera que representa el movimiento. /// Carga la lista objetosCandidatos /// </summary> private void selectPotentialColliders(TgcElipsoid characterElipsoid, Vector3 movementVector, List <Collider> colliders) { //Dejar solo los obstáculos que están dentro del radio de movimiento del elipsoide (lo consideramos una esfera, con su mayor radio) var halfMovementVec = Vector3.Multiply(movementVector, 0.5f); movementSphere.setValues( characterElipsoid.Center + halfMovementVec, halfMovementVec.Length() + characterElipsoid.getMaxRadius() ); //Elegir todos los colliders que pasan un test Sphere-Sphere objetosCandidatos.Clear(); foreach (var collider in colliders) { if (collider.Enable && TgcCollisionUtils.testSphereSphere(movementSphere, collider.BoundingSphere)) { objetosCandidatos.Add(collider); } } }
public override void Init() { //Cargar escenario específico para este ejemplo. Este escenario tiene dos layers: objetos normales y objetos con colisión a nivel de triángulo. //La colisión a nivel de triángulos es costosa. Solo debe utilizarse para objetos puntuales (como el piso). Y es recomendable dividirlo en varios //meshes (y no hacer un único piso que ocupe todo el escenario) var loader = new TgcSceneLoader(); escenario = loader.loadSceneFromFile(MediaDir + "\\MeshCreator\\Scenes\\Mountains\\Mountains-TgcScene.xml"); //Cargar personaje con animaciones var skeletalLoader = new TgcSkeletalLoader(); personaje = skeletalLoader.loadMeshAndAnimationsFromFile( MediaDir + "SkeletalAnimations\\BasicHuman\\BasicHuman-TgcSkeletalMesh.xml", new[] { MediaDir + "SkeletalAnimations\\BasicHuman\\Animations\\Walk-TgcSkeletalAnim.xml", MediaDir + "SkeletalAnimations\\BasicHuman\\Animations\\StandBy-TgcSkeletalAnim.xml", MediaDir + "SkeletalAnimations\\BasicHuman\\Animations\\Jump-TgcSkeletalAnim.xml" }); //Configurar animacion inicial personaje.playAnimation("StandBy", true); //Escalarlo porque es muy grande personaje.Position = new Vector3(0, 1000, -150); //Rotarlo 180° porque esta mirando para el otro lado personaje.rotateY(Geometry.DegreeToRadian(180f)); //BoundingSphere que va a usar el personaje personaje.AutoUpdateBoundingBox = false; characterElipsoid = new TgcElipsoid(personaje.BoundingBox.calculateBoxCenter() + new Vector3(0, 0, 0), new Vector3(12, 28, 12)); jumping = false; //Almacenar volumenes de colision del escenario objetosColisionables.Clear(); foreach (var mesh in escenario.Meshes) { //Los objetos del layer "TriangleCollision" son colisiones a nivel de triangulo if (mesh.Layer == "TriangleCollision") { objetosColisionables.Add(TriangleMeshCollider.fromMesh(mesh)); } //El resto de los objetos son colisiones de BoundingBox. Las colisiones a nivel de triangulo son muy costosas asi que deben utilizarse solo //donde es extremadamente necesario (por ejemplo en el piso). El resto se simplifica con un BoundingBox else { objetosColisionables.Add(BoundingBoxCollider.fromBoundingBox(mesh.BoundingBox)); } } //Crear manejador de colisiones collisionManager = new ElipsoidCollisionManager(); collisionManager.GravityEnabled = true; //Crear linea para mostrar la direccion del movimiento del personaje directionArrow = new TgcArrow(); directionArrow.BodyColor = Color.Red; directionArrow.HeadColor = Color.Green; directionArrow.Thickness = 0.4f; directionArrow.HeadSize = new Vector2(5, 10); //Linea para normal de colision collisionNormalArrow = new TgcArrow(); collisionNormalArrow.BodyColor = Color.Blue; collisionNormalArrow.HeadColor = Color.Yellow; collisionNormalArrow.Thickness = 0.4f; collisionNormalArrow.HeadSize = new Vector2(2, 5); //Caja para marcar punto de colision collisionPoint = TgcBox.fromSize(new Vector3(4, 4, 4), Color.Red); //Configurar camara en Tercer Persona camaraInterna = new TgcThirdPersonCamera(personaje.Position, new Vector3(0, 45, 0), 20, -120); Camara = camaraInterna; //Crear SkyBox skyBox = new TgcSkyBox(); skyBox.Center = new Vector3(0, 0, 0); skyBox.Size = new Vector3(10000, 10000, 10000); var texturesPath = MediaDir + "Texturas\\Quake\\SkyBox3\\"; skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Up, texturesPath + "Up.jpg"); skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Down, texturesPath + "Down.jpg"); skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Left, texturesPath + "Left.jpg"); skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Right, texturesPath + "Right.jpg"); skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Front, texturesPath + "Back.jpg"); skyBox.setFaceTexture(TgcSkyBox.SkyFaces.Back, texturesPath + "Front.jpg"); skyBox.InitSkyBox(); //Modifier para ver BoundingBox Modifiers.addBoolean("Collisions", "Collisions", true); Modifiers.addBoolean("showBoundingBox", "Bouding Box", true); //Modifiers para desplazamiento del personaje Modifiers.addFloat("VelocidadCaminar", 0, 20, 2); Modifiers.addFloat("VelocidadRotacion", 1f, 360f, 150f); Modifiers.addBoolean("HabilitarGravedad", "Habilitar Gravedad", true); Modifiers.addVertex3f("Gravedad", new Vector3(-50, -50, -50), new Vector3(50, 50, 50), new Vector3(0, -4, 0)); Modifiers.addFloat("SlideFactor", 0f, 2f, 1f); Modifiers.addFloat("Pendiente", 0f, 1f, 0.72f); Modifiers.addFloat("VelocidadSalto", 0f, 50f, 10f); Modifiers.addFloat("TiempoSalto", 0f, 2f, 0.5f); UserVars.addVar("Movement"); }