void CollideEntity::bounce(float ba) { if (getState() == STATE_PUSH) { dsq->spawnParticleEffect("HitSurface", dsq->game->lastCollidePosition); //dsq->effectCollisionSmoke(position); sound("RockHit"); // HACK: replace damage function //damage(pushDamage); setState(STATE_PUSHDELAY, 0.3); } switch (bounceType) { case BOUNCE_REAL: { if (!vel.isZero()) { float len = vel.getLength2D(); Vector I = vel/len; Vector N = dsq->game->getWallNormal(dsq->game->lastCollidePosition); if (!N.isZero()) { //2*(-I dot N)*N + I vel = 2*(-I.dot(N))*N + I; vel.setLength2D(len*ba); } } } break; case BOUNCE_SIMPLE: { if (!vel.isZero()) { float len = vel.getLength2D(); Vector mov = vel; mov.setLength2D(len*ba); if (mov.x > mov.y) mov.x = -mov.x; else mov.y = -mov.y; vel = mov; vel.z = 0; } } break; } //mov.setLength2D(-len * ba); onBounce(); }
void GRubberBallSwarm::advanceBall(double* pBallPos) { // Get the ball double* pBallDir = pBallPos + m_dims; double* pBallBias = pBallDir + m_dims; GRubberBallStats* pBallStats = (GRubberBallStats*)(pBallBias + m_dims); // Advance GVec::addScaled(pBallPos, pBallStats->m_speed, pBallDir, m_dims); if(isInside(pBallPos)) { // Accelerate pBallStats->m_speed *= m_acceleration; pBallStats->m_odometer += pBallStats->m_speed; return; } // Back up GVec::addScaled(pBallPos, -pBallStats->m_speed, pBallDir, m_dims); if(!isInside(pBallPos)) { if(GVec::squaredMagnitude(pBallPos, m_dims) == 0) throw Ex("GRubberBallSwarm expects the origin to be inside"); // The shell moved, and the ball is not longer inside, so it must die initBall(pBallPos, pBallStats->m_speed / 2); return; } // Move closer to the wall (using binary search) double d = pBallStats->m_speed; int i; for(i = 0; i < m_wallPrecisionIters; i++) { d *= 0.5; GVec::addScaled(pBallPos, d, pBallDir, m_dims); if(isInside(pBallPos)) pBallStats->m_odometer += d; else GVec::addScaled(pBallPos, -d, pBallDir, m_dims); } // Approximate the wall's normal vector by sampling d *= 4; GVec::setAll(m_pNormal, 0.0, m_dims); int insideCount = 0; for(i = 0; i < m_wallPrecisionIters; i++) { m_pRand->spherical(m_pTemp, m_dims); GVec::addScaled(pBallPos, d, m_pTemp, m_dims); if(isInside(pBallPos)) { GVec::add(m_pNormal, m_pTemp, m_dims); insideCount++; } else GVec::subtract(m_pNormal, m_pTemp, m_dims); GVec::addScaled(pBallPos, -d, m_pTemp, m_dims); } if(insideCount == 0) { // Somehow we got stuck in a bad spot, so let's just kill the ball initBall(pBallPos, 0.1); return; } // Update the ball's bias double sum = 0; for(i = 0; i < m_dims; i++) { m_pTemp[i] = 1.0 / std::max(1e-9, std::abs(m_pNormal[i])); sum += m_pTemp[i]; } GVec::multiply(pBallBias, (1.0 - m_learningRate), m_dims); GVec::addScaled(pBallBias, m_learningRate * m_dims / sum, m_pTemp, m_dims); sum = 0; for(i = 0; i < m_dims; i++) sum += pBallBias[i]; GVec::multiply(pBallBias, (double)m_dims / sum, m_dims); // Bounce in a biassed random direction onBounce(pBallPos); m_pRand->spherical(pBallDir, m_dims); for(i = 0; i < m_dims; i++) pBallDir[i] *= pBallBias[i]; GVec::normalize(pBallDir, m_dims); if(GVec::dotProduct(pBallDir, m_pNormal, m_dims) < 0) GVec::multiply(pBallDir, -1, m_dims); pBallStats->m_speed *= m_deceleration; }