/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.physics.content.actions;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.content.Sprite;
import org.catrobat.catroid.physics.PhysicsBoundaryBox;
import org.catrobat.catroid.physics.PhysicsObject;
import org.catrobat.catroid.physics.PhysicsWorld;
public class IfOnEdgeBouncePhysicsAction extends TemporalAction {
public static final float THRESHOLD_VELOCITY_TO_ACTIVATE_BOUNCE = 10.0f;
private static final float COLLISION_OVERLAP_RANGE_FACTOR = 0.9f;
private Sprite sprite;
private PhysicsWorld physicsWorld;
private void performVerticalRepositioning(float bbLookOffsetX, boolean velocityHighEnoughToCollideAfterRepositioning, boolean correctGravityPresent) {
sprite.look.setXInUserInterfaceDimensionUnit(sprite.look.getXInUserInterfaceDimensionUnit() + bbLookOffsetX);
checkBounceActivation(correctGravityPresent, velocityHighEnoughToCollideAfterRepositioning, sprite, PhysicsBoundaryBox.BoundaryBoxIdentifier.BBI_VERTICAL);
}
private void performHorizontalRepositioning(float bbLookOffsetY, boolean velocityHighEnoughToCollideAfterRepositioning, boolean correctGravityPresent) {
sprite.look.setYInUserInterfaceDimensionUnit(sprite.look.getYInUserInterfaceDimensionUnit() + bbLookOffsetY);
checkBounceActivation(correctGravityPresent, velocityHighEnoughToCollideAfterRepositioning, sprite, PhysicsBoundaryBox.BoundaryBoxIdentifier.BBI_HORIZONTAL);
}
private void checkBounceActivation(boolean correctGravityPresent, boolean velocityHighEnoughToCollideAfterRepositioning, Sprite sprite, PhysicsBoundaryBox.BoundaryBoxIdentifier boundaryBoxIdentifier) {
if ((velocityHighEnoughToCollideAfterRepositioning || correctGravityPresent)) {
physicsWorld.setBounceOnce(sprite, boundaryBoxIdentifier);
}
}
@Override
protected void update(float percent) {
// AABB ... AXIS-ALIGNED-BOUNDING-BOX
Vector2 bbLowerEdge = new Vector2();
Vector2 bbUpperEdge = new Vector2();
PhysicsObject physicsObject = physicsWorld.getPhysicsObject(sprite);
physicsObject.getBoundaryBox(bbLowerEdge, bbUpperEdge);
float bbWidth = bbUpperEdge.x - bbLowerEdge.x;
float bbHeight = bbUpperEdge.y - bbLowerEdge.y;
float bbCenterX = bbLowerEdge.x + bbWidth / 2;
float bbCenterY = bbLowerEdge.y + bbHeight / 2;
int vsWidth = ProjectManager.getInstance().getCurrentProject().getXmlHeader().virtualScreenWidth;
int vsHeight = ProjectManager.getInstance().getCurrentProject().getXmlHeader().virtualScreenHeight;
float leftCollisionAreaInnerBorder = (-vsWidth / 2.0f) + (bbWidth / 2.0f);
float leftCollisionAreaOuterBorder = leftCollisionAreaInnerBorder + COLLISION_OVERLAP_RANGE_FACTOR * (-bbWidth);
boolean leftVelocityHighEnoughToCollideAfterRepositioning = physicsObject.getVelocity().x <= -THRESHOLD_VELOCITY_TO_ACTIVATE_BOUNCE;
boolean leftGravityPresent = physicsWorld.getGravity().x < 0;
float rightCollisionAreaInnerBorder = (vsWidth / 2.0f) - (bbWidth / 2.0f);
float rightCollisionAreaOuterBorder = rightCollisionAreaInnerBorder + COLLISION_OVERLAP_RANGE_FACTOR * bbWidth;
boolean rightVelocityHighEnoughToCollideAfterRepositioning = physicsObject.getVelocity().x >= THRESHOLD_VELOCITY_TO_ACTIVATE_BOUNCE;
boolean rightGravityPresent = physicsWorld.getGravity().x > 0;
if (leftCollisionAreaOuterBorder < bbCenterX && bbCenterX < leftCollisionAreaInnerBorder) {
float bbLookOffsetX = Math.abs(bbCenterX - (bbWidth / 2.0f) + (vsWidth / 2.0f));
performVerticalRepositioning(bbLookOffsetX, leftVelocityHighEnoughToCollideAfterRepositioning, leftGravityPresent);
} else if (rightCollisionAreaOuterBorder > bbCenterX && bbCenterX > rightCollisionAreaInnerBorder) {
float bbLookOffsetX = Math.abs(bbCenterX + (bbWidth / 2.0f) - (vsWidth / 2.0f));
performVerticalRepositioning(-bbLookOffsetX, rightVelocityHighEnoughToCollideAfterRepositioning, rightGravityPresent);
}
float bottomCollisionAreaInnerBorder = (-vsHeight / 2.0f) + (bbHeight / 2.0f);
float bottomCollisionAreaOuterBorder = bottomCollisionAreaInnerBorder + COLLISION_OVERLAP_RANGE_FACTOR * (-bbHeight);
boolean bottomVelocityHighEnoughToCollideAfterRepositioning = physicsObject.getVelocity().y <= -THRESHOLD_VELOCITY_TO_ACTIVATE_BOUNCE;
boolean bottomGravityPresent = physicsWorld.getGravity().y < 0;
float topCollisionAreaInnerBorder = (vsHeight / 2.0f) - (bbHeight / 2.0f);
float topCollisionAreaOuterBorder = topCollisionAreaInnerBorder + COLLISION_OVERLAP_RANGE_FACTOR * bbHeight;
boolean topVelocityHighEnoughToCollideAfterRepositioning = physicsObject.getVelocity().y >= THRESHOLD_VELOCITY_TO_ACTIVATE_BOUNCE;
boolean topGravityPresent = physicsWorld.getGravity().y > 0;
if (bottomCollisionAreaOuterBorder < bbCenterY && bbCenterY < bottomCollisionAreaInnerBorder) {
float bbLookOffsetY = Math.abs(bbCenterY - (bbHeight / 2.0f) + (vsHeight / 2.0f));
performHorizontalRepositioning(bbLookOffsetY, bottomVelocityHighEnoughToCollideAfterRepositioning, bottomGravityPresent);
} else if (topCollisionAreaOuterBorder > bbCenterY && bbCenterY > topCollisionAreaInnerBorder) {
float bbLookOffsetY = Math.abs(bbCenterY + (bbHeight / 2.0f) - (vsHeight / 2.0f));
performHorizontalRepositioning(-bbLookOffsetY, topVelocityHighEnoughToCollideAfterRepositioning, topGravityPresent);
}
}
public void setSprite(Sprite sprite) {
this.sprite = sprite;
}
public void setPhysicsWorld(PhysicsWorld physicsWorld) {
this.physicsWorld = physicsWorld;
}
}