// This function handles the collision between the ball and a wall void World::wall_collide_handler(const CollisionEntry& colEntry) { // First we calculate some numbers we need to do a reflection NodePath renderNp = m_windowFrameworkPtr->get_render(); // The normal of the wall LVector3f norm = colEntry.get_surface_normal(renderNp) * -1; // The current speed float curSpeed = m_ballV.length(); // The direction of travel LVector3f inVec = m_ballV / curSpeed; // Angle of incidence float velAngle = norm.dot(inVec); LPoint3f hitDir = colEntry.get_surface_point(renderNp) - m_ballRootNp.get_pos(); hitDir.normalize(); // The angle between the ball and the normal float hitAngle = norm.dot(hitDir); // Ignore the collision if the ball is either moving away from the wall // already (so that we don't accidentally send it back into the wall) // and ignore it if the collision isn't dead-on (to avoid getting caught on // corners) if(velAngle > 0 && hitAngle > .995) { // Standard reflection equation LVector3f reflectVec = (norm * norm.dot(inVec * -1) * 2) + inVec; // This makes the velocity half of what it was if the hit was dead-on // and nearly exactly what it was if this is a glancing blow m_ballV = reflectVec * (curSpeed * (((1-velAngle)*.5)+.5)); // Since we have a collision, the ball is already a little bit buried in // the wall. This calculates a vector needed to move it so that it is // exactly touching the wall LPoint3f disp = (colEntry.get_surface_point(renderNp) - colEntry.get_interior_point(renderNp)); LPoint3f newPos = m_ballRootNp.get_pos() + disp; m_ballRootNp.set_pos(newPos); } }
// Accepts arrow keys to move either the player or the menu cursor, // Also deals with grid checking and collision detection void World::move() { // If the camera-left key is pressed, move camera left. // If the camera-right key is pressed, move camera right. NodePath cameraNp = m_windowFrameworkPtr->get_camera_group(); cameraNp.look_at(m_ralph); if(m_keyMap[K_cam_left] != false) { cameraNp.set_x(cameraNp, -20 * ClockObject::get_global_clock()->get_dt()); } if(m_keyMap[K_cam_right] != false) { cameraNp.set_x(cameraNp, +20 * ClockObject::get_global_clock()->get_dt()); } // save ralph's initial position so that we can restore it, // in case he falls off the map or runs into something. LPoint3f startPos = m_ralph.get_pos(); // If a move-key is pressed, move ralph in the specified direction. if(m_keyMap[K_left]) { m_ralph.set_h(m_ralph.get_h() + 300 * ClockObject::get_global_clock()->get_dt()); } if(m_keyMap[K_right]) { m_ralph.set_h(m_ralph.get_h() - 300 * ClockObject::get_global_clock()->get_dt()); } if(m_keyMap[K_forward]) { m_ralph.set_y(m_ralph, -25 * ClockObject::get_global_clock()->get_dt()); } // If ralph is moving, loop the run animation. // If he is standing still, stop the animation. if(m_keyMap[K_forward] || m_keyMap[K_left] || m_keyMap[K_right]) { if(!m_isMoving) { m_ralph.loop("run", true); m_isMoving = true; } } else { if(m_isMoving) { m_ralph.stop("run"); m_ralph.pose("walk", 5); m_isMoving = false; } } // If the camera is too far from ralph, move it closer. // If the camera is too close to ralph, move it farther. LPoint3f camVec = m_ralph.get_pos() - cameraNp.get_pos(); camVec.set_z(0); float camDist = camVec.length(); camVec.normalize(); if(camDist > 10.0) { cameraNp.set_pos(cameraNp.get_pos() + camVec*(camDist-10)); camDist = 10.0; } if(camDist < 5.0) { cameraNp.set_pos(cameraNp.get_pos() - camVec*(5-camDist)); camDist = 5.0; } // Now check for collisions. NodePath renderNp = m_windowFrameworkPtr->get_render(); m_collisionTraverser.traverse(renderNp); // Adjust ralph's Z coordinate. If ralph's ray hit terrain, // update his Z. If it hit anything else, or didn't hit anything, put // him back where he was last frame. m_ralphGroundHandlerPtr->sort_entries(); if(m_ralphGroundHandlerPtr->get_num_entries() > 0 && m_ralphGroundHandlerPtr->get_entry(0)->get_into_node()->get_name() == "terrain") { m_ralph.set_z(m_ralphGroundHandlerPtr->get_entry(0)->get_surface_point(renderNp).get_z()); } else { m_ralph.set_pos(startPos); } // Keep the camera at one foot above the terrain, // or two feet above ralph, whichever is greater. m_camGroundHandlerPtr->sort_entries(); if(m_camGroundHandlerPtr->get_num_entries() > 0 && m_camGroundHandlerPtr->get_entry(0)->get_into_node()->get_name() == "terrain") { cameraNp.set_z(m_camGroundHandlerPtr->get_entry(0)->get_surface_point(renderNp).get_z()+1.0); } if(cameraNp.get_z() < m_ralph.get_z() + 2.0) { cameraNp.set_z(m_ralph.get_z() + 2.0); } // The camera should look in ralph's direction, // but it should also try to stay horizontal, so look at // a floater which hovers above ralph's head. m_floaterNp.set_pos(m_ralph.get_pos()); m_floaterNp.set_z(m_ralph.get_z() + 2.0); cameraNp.look_at(m_floaterNp); }