Here's a youtube of the build before I put the pathing in: http://www.youtube.com/watch?v=Gds9Q9z_CsY So a while back I was determined to get a Sonic engine working on Unity3D- it was going to be the editor I wanted to learn how to use (maybe still is). At the time, and even now, there still isn't a good Sonic controller for Unity3D, I think one or two have been made privately, but I think we're in dire need of a 3D engine by now, unfortunately, the math/physics just got the best of me- I ported this code to C# later on but I don't know what state it's in so I'm posting my unityscript backup. Don't expect too much :P but here's what I got working before I had exams: - Align to ground normal - Right now it only uses a single vector- but I was told (can't remember who) that if you use 3 vectors, you can average them and get really smooth transitions across every normal, instead of the snapping I have here. - Use the Sonic BGE (cyborg_ar) style raised-wall pathing system. - Basically if you raise two walls on either side of a path, the vectors that shoot out on either side of your character will detect them and align the character to the center, as well as align him to the normal of the ground. After this I realized I had to get the physics working- that's where I got stuck. Something about dot product of gravity and the ground normal I dunno, but my approach was going to be to say screw-it to the physics engine and handle it all in code with basic newtonian formulas that I could modify during pathing for loops and such. A friend of mine was approaching form a standpoint of guiding the physics engine along, using helper-forces. Personally I really wanted to do bSplines like Damizean did with Eggen but that's just so beyond me :P Either way, I just wanted to post this in case someone had tried it, gotten stuck, and gave up before they really had a chance to shine. I had to go to the unity channel for that axis-alignment stuff, because one function worked and another didn't and... whatever. I would really like to see an open source Sonic engine in Unity3D, I think it's the most approachable IDE for the beginning fangamer. SonicGDK is excellent, I'm really impressed with those guys, and I may have to bite the bullet and learn UDK, but that several gig download and high computing requirement is just overkill for what I wanted. I was happy with basic GL shading and materials, and a light footprint (not to mention Unity has a web plugin, which would make it -real- handy for SAGE). If it looks like I didn't know what I was doing- you're right. But at least I commented like a mofo Code (Text): private var controller :CharacterController; controller = gameObject.GetComponent(CharacterController); var hit : RaycastHit; // cast a ray down from the player object var hit_left : RaycastHit; // 2.5D Directional vector for path system var hit_right : RaycastHit; // 2.5D Directional vector for path system var direction_vector : Vector3; var yRot : float; // Internal storage of direction vector because FromToRotation sets alignment axis to angle 0 var playerSpeed : float = 200.0; var rotSpeed : float = 80.0; var vector_length : float = 23.0; var is2D : boolean = false; var groundRay : Ray; groundRay = new Ray(transform.position, Vector3.down); function FixedUpdate() { var forward : Vector3 = transform.TransformDirection(Vector3.forward); var down : Vector3 = transform.TransformDirection(Vector3.down); var left : Vector3 = transform.TransformDirection(Vector3.left); var right : Vector3 = transform.TransformDirection(Vector3.right); // Draw debug vectors to see wall collisions //~ Debug.DrawRay(transform.position, down*vector_length, Color.green); // Down vector //~ Debug.DrawRay(transform.position, left*vector_length, Color.green); //~ Debug.DrawRay(transform.position, right*vector_length, Color.green); if (Physics.Raycast(transform.position, down, hit)) { // Keep alignment to ground. transform.rotation = Quaternion.FromToRotation(Vector3.up, hit.normal); // Get ground normal and align to it //transform.rotation = Quaternion.LookRotation(Vector3.forward, hit.normal); // Also works but breaks on the loop transform.position = hit.point + transform.TransformDirection(Vector3.up*5.0); // Move character 5.0 above the normal hit point // Begin Path System if(Physics.Raycast(transform.position, left, hit_left, vector_length) && Physics.Raycast(transform.position, right, hit_right, vector_length)) { //direction_vector = transform.TransformDirection((hit_left.normal - hit_right.normal)/2.0); //direction_vector = hit_left.normal - hit_right.normal; //direction_vector.Normalize(); //direction_vector = hit_left.point - hit_right.point; //direction_vector.Normalize(); Debug.DrawLine(hit_left.point, transform.position, Color.white); Debug.DrawLine(hit_right.point, transform.position, Color.white); direction_vector = Vector3( hit_left.normal.x - hit_right.normal.x, hit_left.normal.y - hit_right.normal.y, hit_left.normal.z - hit_right.normal.z); direction_vector.Normalize(); //direction_vector = Vector3.Cross(direction_vector, hit.normal); var midPoint : Vector3 = (hit_left.point + hit_right.point) / 2.0; transform.Translate(midPoint-transform.position, Space.World); //direction_vector = transform.TransformDirection(direction_vector); Debug.DrawRay(transform.position, direction_vector*vector_length, Color.red); var dirVec : Vector3 = Vector3.Cross(direction_vector, hit.normal); Debug.DrawRay(transform.position, dirVec*vector_length, Color.blue); //transform.LookAt(transform.TransformDirection(dirVec)); //transform.LookAt(transform.TransformDirection(direction_vector)); // Programming note: When using transform.TransformDirection(), player gets stuck trying to point down transform.rotation = Quaternion.LookRotation(dirVec, hit.normal); // Points player in direction for 2D path system yRot = transform.rotation.eulerAngles.y; // Make sure the last angle the player leaves the path as, is the one he's in in 3D mode, instead of returning to last 3D yRot. } else { // If we don't put this in the else block, then it conflicts with the rotation genrated by 2D detection. transform.rotation = transform.rotation * Quaternion.AngleAxis(yRot, Vector3.up); // Reapply direction so that we can turn. } } // Note: May need to rewrite the translation step as one single statement so that it doesn't desynch the 2D controls above. if(Input.GetKey("up")) { transform.Translate(0.0,0.0, playerSpeed * Time.deltaTime); } else if(Input.GetKey("down")) { transform.Translate(0.0,0.0, -1 * playerSpeed * Time.deltaTime); } else { controller.SimpleMove(Vector3.zero); } if(Input.GetKey("left")) { yRot = yRot + -1*200*Time.deltaTime; } if(Input.GetKey("right")) { yRot = yRot + 200*Time.deltaTime; } } @script RequireComponent(CharacterController)
Nothing really catches my eye except for the thing about smoothing the movement with 3 vectors. Where do you get the other 2 vectors from, a front and back raycast? I've been trying to figure out how to smooth it for a while and this mightjust be it.
I would offset the the vectors from the center vector, front and back, and cast straight down. Having 2 more, one on each side, may help stabilize side to side.
Well the EASIEST way would be to use per vertex normals and the barycentric coordinates as weighting between them that would be accurate and fairly cheap. I've been looking at the unity script reference and can't find any info if vertex normals are even stored just face normals. But if you can find something I can't it would be matter of finding sonic's position on the current triangle (there is a built in function for that) which would give you weights to interpolate the current up angle