/** * Returns the angle in degrees between the two vectors */ kmScalar kmVec2DegreesBetween(const kmVec2* v1, const kmVec2* v2) { if(kmVec2AreEqual(v1, v2)) { return 0.0; } kmVec2 t1, t2; kmVec2Normalize(&t1, v1); kmVec2Normalize(&t2, v2); kmScalar cross = kmVec2Cross(&t1, &t2); kmScalar dot = kmVec2Dot(&t1, &t2); /* * acos is only defined for -1 to 1. Outside the range we * get NaN even if that's just because of a floating point error * so we clamp to the -1 - 1 range */ if(dot > 1.0) dot = 1.0; if(dot < -1.0) dot = -1.0; return kmRadiansToDegrees(atan2(cross, dot)); }
void calculate_line_normal(kmVec2 p1, kmVec2 p2, kmVec2 other_point, kmVec2* normal_out) { /* A = (3,4) B = (2,1) C = (1,3) AB = (2,1) - (3,4) = (-1,-3) AC = (1,3) - (3,4) = (-2,-1) N = n(AB) = (-3,1) D = dot(N,AC) = 6 + -1 = 5 since D > 0: N = -N = (3,-1) */ kmVec2 edge, other_edge; kmScalar d; kmVec2 n; kmVec2Subtract(&edge, &p2, &p1); kmVec2Subtract(&other_edge, &other_point, &p1); kmVec2Normalize(&edge, &edge); kmVec2Normalize(&other_edge, &other_edge); n.x = edge.y; n.y = -edge.x; d = kmVec2Dot(&n, &other_edge); if(d > 0.0f) { n.x = -n.x; n.y = -n.y; } normal_out->x = n.x; normal_out->y = n.y; kmVec2Normalize(normal_out, normal_out); }
void BattleManagerScreen::onEntityEffectEvent( CCObject* e ) { GameEntityEffectEvt* evt = dynamic_cast<GameEntityEffectEvt*>(e); if(!evt) return; if( evt->name.compare("Death Grip") == 0 ) { CCLog("death grip"); kmVec2 pOrigin; kmVec2 pTarget; if( !GetEntityPosition(evt->origin, pOrigin) || !GetEntityPosition(evt->target, pTarget) ) { CCLog("aborting death grip evt due to invalid target(s)"); } kmVec2 toTarget; kmVec2Subtract(&toTarget, &pTarget, &pOrigin); kmVec2 u_toTarget; kmVec2Normalize(&u_toTarget, &toTarget); float leashDistance = 1; //one game unit kmVec2 dv; kmVec2Scale(&dv, &u_toTarget, leashDistance ); kmVec2 posEnd; kmVec2Add(&posEnd, &pOrigin, &dv); //convert back to screen coordinates kmVec2Scale(&posEnd, &posEnd, VIEW_UNIT_CONVERSION); GameEntityView* tView = getViewForEntity(evt->target); tView->setPosition(posEnd.x, posEnd.y); } }
void BattleManagerScreen::enemyMovementAI( int enemyIdx, float dt ) { float speed = 75.0f; //5 pixels/sec EntityPair& enemy = m_enemies[enemyIdx]; std::vector<kmVec2> impulses; //x, y std::vector<float> impulseWeights; //TODO: select from closest player EntityPair& player = m_players[0]; CCSize eSize = enemy.view->getContentSize(); CCPoint ePos = enemy.view->getPosition(); ePos.x += eSize.width/2; ePos.y += eSize.height/2; //convert to center origin CCSize pSize = player.view->getContentSize(); CCPoint pPos = player.view->getPosition(); pPos.x += pSize.width/2; pPos.y += pSize.height/2; //convert to center origin float playerLeash = pSize.width * 1.1f; float playerLeashSq = playerLeash*playerLeash; //impulse towards the player kmVec2 toPlayer = { pPos.x - ePos.x, pPos.y - ePos.y }; kmVec2 u_toPlayer; kmVec2Normalize( &u_toPlayer, &toPlayer); if( kmVec2LengthSq( &toPlayer ) < (playerLeashSq * 0.75f) ) { //impulse away from player (too close) kmVec2 u_fromPlayer; kmVec2Scale(&u_fromPlayer, &u_toPlayer, -1*0.75f); //flip the 'to player' vector impulses.push_back(u_fromPlayer); impulseWeights.push_back(100); //vastly overweigh the impulse to the player }else { if( kmVec2LengthSq(&toPlayer) > playerLeashSq ) { //kmVec2Scale(&u_toPlayer, &u_toPlayer, 0.51f); //impulse towards player impulses.push_back(u_toPlayer); impulseWeights.push_back(100); } } //add impulses away from other enemies for( int i=0; i< m_enemies.size(); i++) { if( i == enemyIdx ) continue; CCSize nSize = m_enemies[i].view->getContentSize(); CCPoint nPos = m_enemies[i].view->getPosition(); nPos.x += nSize.width/2; nPos.y += nSize.height/2; kmVec2 toNeighbor = { nPos.x - ePos.x, nPos.y - ePos.y }; float neighborLeash = nSize.width; if( kmVec2LengthSq(&toNeighbor) < neighborLeash * neighborLeash ) { kmVec2 u_toNeighbor; kmVec2Normalize(&u_toNeighbor, &toNeighbor); kmVec2 u_fromNeighbor; kmVec2Scale(&u_fromNeighbor, &u_toNeighbor, -1); impulses.push_back(u_fromNeighbor); impulseWeights.push_back(50); } } if( impulses.size() == 0 ) return; //blend impulses kmVec2 finalImpulse = impulses[0]; //zero always valid because its the impulse to the player float finalImpulseWeight = impulseWeights[0]; for( int i=1; i< impulses.size(); i++) { float w1 = finalImpulseWeight; float w2 = impulseWeights[i]; float wTot = w1 + w2; kmVec2 blend; blend.x = (finalImpulse.x * w1 / wTot) + (impulses[i].x * w2 / wTot); blend.y = (finalImpulse.y * w1 / wTot) + (impulses[i].y * w2 / wTot); //run-length summation finalImpulseWeight = wTot; finalImpulse = blend; } kmVec2 scaledImpulse; kmVec2Scale(&scaledImpulse, &finalImpulse, speed * dt); //CCLog("impulse %.4f", kmVec2Length(& finalImpulse)); if( kmVec2Length(& finalImpulse) < (0.5f) ) { //CCLog("ignore impulse %.4f", kmVec2Length(& finalImpulse)); return; //ignore very small changes to avoid leash jitter } ePos.x += scaledImpulse.x; ePos.y += scaledImpulse.y; ePos.x -= eSize.width/2; ePos.y -= eSize.height/2; //back to original anchor coords enemy.view->setPosition(ePos); }