void LaunchCallbacks(CharacterRay2D[] rays, RayType rayType) { m_curHits.Clear(); for (int i = 0; i < rays.Length; i++) { CharacterRay2D curRay = rays[i]; // do not send message twice for same collider & same raytype if (curRay.m_collider != null && !m_curHits.Contains(curRay.m_collider)) { m_curHits.Add(curRay.m_collider); APHitable hitable = curRay.m_collider.GetComponent <APHitable>(); if (hitable) { hitable.OnCharacterTouch(m_character, rayType); } } } }
public void OnTriggerEnter2D(Collider2D otherCollider) { // make sure we are not touching launcher itself if (m_alive && (otherCollider.gameObject != m_launcher.gameObject) && (otherCollider.isTrigger != m_ignoreTrigger)) { // alert hitable if any APHitable hitable = otherCollider.GetComponent <APHitable>(); if (hitable != null) { // ignore hit if requested if (!hitable.OnBulletHit(m_launcher, this)) { return; } } // launch anim if (!string.IsNullOrEmpty(m_animStateExplode)) { m_anim.Play(m_animStateExplode); } // stop move & destroy if (m_explodeOnTouch) { if (m_stopOnExplode) { m_curVel = Vector2.zero; if (m_rigidBody) { m_rigidBody.velocity = Vector2.zero; } } m_alive = false; Object.Destroy(gameObject, m_destroyTimer); } } }
public virtual void OnAttackMeleeHit(APAttack attack, APHitable hitObject) { }
bool MoveHorizontal(ref Vector2 moveOffset, bool bFront, bool bCorrectPos) { bool bPenetrated = false; float fMinPenetration = 0f; bool bRayDirRight = (m_faceRight && bFront) || (!m_faceRight && !bFront); Vector2 rayDir = bRayDirRight ? transform.right : -transform.right; float fMoveOffsetOnRay = Vector2.Dot(moveOffset, rayDir); bool bCorrectVel = fMoveOffsetOnRay > 0f; float fMaxAllowedMove = Mathf.Abs(fMoveOffsetOnRay); Vector2 v2SavedPos = transform.position; transform.position = m_curPos; // Launch ground cast from destination position CharacterRay2D[] rays = bFront ? m_RaysFront : m_RaysBack; foreach (CharacterRay2D curRay in rays) { float fRayPen = curRay.m_penetration * Mathf.Abs(transform.lossyScale.x) * m_scale.x; float fCurRayLength = fRayPen + curRay.m_extraDistance; // add move offset if needed if (bCorrectVel) { fCurRayLength += Mathf.Abs(fMoveOffsetOnRay); } // launch ray Vector2 rayOrigin = GetRayPositionWs(curRay); if (m_DrawRays) { Debug.DrawLine(rayOrigin, rayOrigin + rayDir * fCurRayLength); } int hitCount = Physics2D.RaycastNonAlloc(rayOrigin, rayDir, m_rayResults, fCurRayLength, BuildRayLayer()); float fClosestHit = float.MaxValue; for (int hitId = 0; hitId < hitCount; hitId++) { RaycastHit2D hit = m_rayResults[hitId]; // ignore character himself if (hit.rigidbody == m_rigidBody) { continue; } // ignore triggers & handle ignored colliders if (hit.collider != null) { if (hit.collider.isTrigger || IsIgnoredCollider(hit.collider)) { continue; } } // compute distance of penetration float fHitLength = hit.fraction * fCurRayLength; float fPen = fHitLength - fRayPen; // launch callback APMaterial hitMaterial = null; if (hit.collider != null) { // handle onewayground here if (IsOneWayGround(hit.collider)) { HandleOneWayGround(fPen, fRayPen, hit.collider); // ignore collision in all cases continue; } // handle callbacks hitMaterial = hit.collider.GetComponent <APMaterial>(); APHitable hitable = hit.collider.GetComponent <APHitable>(); if (hitable && m_character) { // ignore hit if requested if (!hitable.OnCharacterTouch(m_character, bFront ? RayType.Front : RayType.Back, hit, fPen, hitMaterial)) { continue; } } } // ignore insides (we don't know in which direction we should correct penetration) if (hit.fraction == 0f) { continue; } // ignore non opposing normal against ray if (Vector2.Dot(rayDir, hit.normal) > 0f) { continue; } if (m_DrawRays) { Debug.DrawLine(hit.point, hit.point + hit.normal * 0.1f, Color.red); } // we are in skin width if (bFront) { m_touchFront = true; } else { m_touchBack = true; } // keep hit with maximum penetration (allow small penetration to prevent djitering) if ((fPen + m_allowedPenetration) < fMinPenetration) { bPenetrated = true; fMinPenetration = fPen; } // save collider of closest hit in skin width if (hit.fraction < fClosestHit) { fClosestHit = hit.fraction; curRay.m_hitInfo.m_collider = hit.collider; curRay.m_hitInfo.m_normal = hit.normal; curRay.m_hitInfo.m_penetration = fPen; curRay.m_hitInfo.m_material = hitMaterial; } // Compute velocity correction if (bCorrectVel) { fMaxAllowedMove = Mathf.Min(fMaxAllowedMove, fHitLength - fRayPen); fMaxAllowedMove = Mathf.Max(0f, fMaxAllowedMove); // cannot be negative } } } Vector2 fPrevPos = m_curPos; // correction by velocity if (bCorrectVel) { moveOffset += rayDir * (fMaxAllowedMove - fMoveOffsetOnRay); // integrate m_curPos += rayDir * (fMaxAllowedMove); // prevent carrier velocity from adding inertia if we did not touch anything if (m_character.IsCarried()) { float fDot = Vector2.Dot(m_carrierVel, rayDir); m_posError += rayDir * fDot * Time.deltaTime; } } // correction by position if (bPenetrated && bCorrectPos) { Vector2 v2PosErr = Mathf.Max(fMinPenetration, -m_PosErrorMaxVel * Time.deltaTime) * rayDir; m_curPos += v2PosErr; // keep this value m_posError += v2PosErr; } // fix all rays penetration if (bFront) { foreach (CharacterRay2D curRay in m_RaysFront) { if (curRay.m_hitInfo.m_collider) { curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir); } } } else { foreach (CharacterRay2D curRay in m_RaysBack) { if (curRay.m_hitInfo.m_collider) { curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir); } } } transform.position = v2SavedPos; return(bPenetrated); }
bool MoveVertical(ref Vector2 moveOffset, bool bDown, bool bCorrectPos) { bool bPenetrated = false; float fMinPenetration = 0f; Vector2 rayDir = bDown ? -transform.up : transform.up; float fMoveOffsetOnRay = Vector2.Dot(moveOffset, rayDir); float fMaxAllowedMove = Mathf.Abs(fMoveOffsetOnRay); bool bCorrectVel = fMoveOffsetOnRay > 0f; float fGroundBounciness = 0f; float fClosestGroundHit = float.MaxValue; Vector2 v2SavedPos = transform.position; transform.position = m_curPos; if (bDown) { m_touchGround = false; m_groundGameObject = null; m_distToGround = 0f; } // Launch ground cast from destination position CharacterRay2D[] rays = bDown ? m_RaysGround : m_RaysUp; foreach (CharacterRay2D curRay in rays) { float fRayPen = curRay.m_penetration * Mathf.Abs(transform.lossyScale.y) * m_scale.y; float fCurRayLength = fRayPen + (Mathf.Max(curRay.m_extraDistance, 1f)); // make sure ground is detected far enough for slope down snapping // add move offset in raycast if needed if (bCorrectVel) { fCurRayLength += Mathf.Abs(fMoveOffsetOnRay); } // launch ray Vector2 rayOrigin = GetRayPositionWs(curRay); if (m_DrawRays) { Debug.DrawLine(rayOrigin, rayOrigin + rayDir * fCurRayLength); } int hitCount = Physics2D.RaycastNonAlloc(rayOrigin, rayDir, m_rayResults, fCurRayLength, BuildRayLayer()); float fClosestHit = float.MaxValue; for (int hitId = 0; hitId < hitCount; hitId++) { RaycastHit2D hit = m_rayResults[hitId]; // ignore character himself if (hit.rigidbody == m_rigidBody) { continue; } // ignore triggers & handle ignored colliders if (hit.collider != null) { if (hit.collider.isTrigger || IsIgnoredCollider(hit.collider)) { continue; } } // compute distance of penetration float fHitLength = hit.fraction * fCurRayLength; float fPen = fHitLength - fRayPen; // launch callback APMaterial hitMaterial = null; if (hit.collider != null) { // Handle oneway ground here if (IsOneWayGround(hit.collider)) { HandleOneWayGround(fPen, fRayPen, hit.collider); // ignore if we are colliding under or if high penetrattion occurred with this collider if (!bDown || (bDown && ((hit.normal.y < 0f) || m_oneWayColliders.Exists(x => x.m_collider == hit.collider)))) { continue; } } // Handle callbacks hitMaterial = hit.collider.GetComponent <APMaterial>(); APHitable hitable = hit.collider.GetComponent <APHitable>(); if (hitable && m_character) { if (!hitable.OnCharacterTouch(m_character, bDown ? RayType.Ground : RayType.Up, hit, fPen, hitMaterial)) { continue; } } } // ignore insides (we don't know in which direction we should correct penetration) if (hit.fraction == 0f) { continue; } // ignore non opposing normal against ray if (Vector2.Dot(rayDir, hit.normal) > 0f) { continue; } if (m_DrawRays) { Debug.DrawLine(hit.point, hit.point + hit.normal * 0.1f, Color.red); } // keep hit with maximum penetration if ((fPen + m_allowedPenetration) < fMinPenetration) { bPenetrated = true; fMinPenetration = fPen; } // Compute velocity correction if (bCorrectVel) { fMaxAllowedMove = Mathf.Min(fMaxAllowedMove, fHitLength - fRayPen); fMaxAllowedMove = Mathf.Max(0f, fMaxAllowedMove); // cannot be negative } // we are in skin width if (bDown) { m_touchGround = true; // keep ground normal with most penetration if (fPen < fClosestGroundHit) { m_groundNormal = hit.normal; fClosestGroundHit = fPen; m_groundGameObject = hit.collider.gameObject; m_distToGround = fPen; // save ground bounciness if (hitMaterial != null) { fGroundBounciness = Mathf.Max(fGroundBounciness, hitMaterial.m_groundBounciness); } } } else { m_touchHead = true; } // save collider of closest hit for this ray if (hit.fraction < fClosestHit) { fClosestHit = hit.fraction; curRay.m_hitInfo.m_collider = hit.collider; curRay.m_hitInfo.m_normal = hit.normal; curRay.m_hitInfo.m_penetration = fPen; curRay.m_hitInfo.m_material = hitMaterial; } } } Vector2 fPrevPos = m_curPos; // prevent velocity along ray axis if (bCorrectVel) { moveOffset += rayDir * (fMaxAllowedMove - fMoveOffsetOnRay); // integrate m_curPos += rayDir * (fMaxAllowedMove); // prevent carrier velocity from adding inertia if we did not touch anything if (m_character.IsCarried()) { float fDot = Vector2.Dot(m_carrierVel, rayDir); m_posError += rayDir * fDot * Time.deltaTime; } } // correction by position if (bPenetrated && bCorrectPos) { Vector2 v2PosErr = Mathf.Max(fMinPenetration, -m_PosErrorMaxVel * Time.deltaTime) * rayDir; m_curPos += v2PosErr; // keep this value so it is removed from velocity computing later m_posError += v2PosErr; } // fix distance to ground if (bDown && m_touchGround) { m_groundNormalLs = transform.InverseTransformDirection(m_groundNormal); m_distToGround += Vector2.Dot(fPrevPos - m_curPos, rayDir); } // fix all rays penetration if (bDown) { foreach (CharacterRay2D curRay in m_RaysGround) { if (curRay.m_hitInfo.m_collider) { curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir); } } } else { foreach (CharacterRay2D curRay in m_RaysUp) { if (curRay.m_hitInfo.m_collider) { curRay.m_hitInfo.m_penetration += Vector2.Dot(fPrevPos - m_curPos, rayDir); } } } // Handle ground bounciness here if (m_distToGround < 0.01f && fGroundBounciness > 0f) { Vector2 v2ImpulseDir = m_groundNormal; m_character.AddImpulse(v2ImpulseDir * fGroundBounciness * 30f); } transform.position = v2SavedPos; return(bPenetrated); }
void StateMeleeAttack(APFsmStateEvent eEvent) { switch (eEvent) { case APFsmStateEvent.eEnter: { SendMessage("APOnCharacterMeleeAttack", SendMessageOptions.DontRequireReceiver); PlayAnim(m_curAttackAnimHash, 0f); // clear buffer of hits & disable hit zones at init APMeleeAttack curAttack = m_meleeAttacks.m_attacks[m_curAttackId]; foreach (APHitZone curHitZone in curAttack.m_hitZones) { curHitZone.attackHits.Clear(); } } break; case APFsmStateEvent.eUpdate: { // update state ApplyGravity(); HandleHorizontalMove(); // Compute all hits for current attack APMeleeAttack curAttack = m_meleeAttacks.m_attacks [m_curAttackId]; foreach (APHitZone curHitZone in curAttack.m_hitZones) { if (curHitZone.m_active && curHitZone.gameObject.activeInHierarchy) { Vector2 wsPos = curHitZone.transform.position; int hitCount = Physics2D.OverlapCircleNonAlloc(wsPos, curHitZone.m_radius, m_attackHitResult, m_motor.m_rayLayer); for (int i = 0; i < hitCount; i++) { Collider2D curHit = m_attackHitResult[i]; // notify only hitable objects and not already hit by this hit zone APHitable hitable = curHit.GetComponent <APHitable>(); if (hitable != null && !curHitZone.attackHits.Contains(hitable)) { curHitZone.attackHits.Add(hitable); // alert hitable hitable.OnMeleeAttackHit(this, curHitZone); } } } } // make sure state does not end infinitely if (m_fsm.GetFsmStateTime() > m_maxAttackDuration) { SetState(GetPreviousState()); } } break; case APFsmStateEvent.eLeave: { m_curAttackId = -1; } break; } }