|
Android example source code file (Ball.java)
The Ball.java Android example source code/* * Copyright (C) 2008 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.divideandconquer; /** * A ball has a current location, a trajectory angle, a speed in pixels per * second, and a last update time. It is capable of updating itself based on * its trajectory and speed. * * It also knows its boundaries, and will 'bounce' off them when it reaches them. */ public class Ball extends Shape2d { private long mLastUpdate; private float mX; private float mY; private double mAngle; private final float mPixelsPerSecond; private final float mRadiusPixels; private Shape2d mRegion; private Ball(long now, float pixelsPerSecond, float x, float y, double angle, float radiusPixels) { mLastUpdate = now; mPixelsPerSecond = pixelsPerSecond; mX = x; mY = y; mAngle = angle; mRadiusPixels = radiusPixels; } public float getX() { return mX; } public float getY() { return mY; } public float getLeft() { return mX - mRadiusPixels; } public float getRight() { return mX + mRadiusPixels; } public float getTop() { return mY - mRadiusPixels; } public float getBottom() { return mY + mRadiusPixels; } public float getRadiusPixels() { return mRadiusPixels; } public double getAngle() { return mAngle; } /** * Get the region the ball is contained in. */ public Shape2d getRegion() { return mRegion; } /** * Set the region that the ball is contained in. * @param region The region. */ public void setRegion(Shape2d region) { if (mX < region.getLeft()) { mX = region.getLeft(); bounceOffLeft(); } else if (mX > region.getRight()) { mX = region.getRight(); bounceOffRight(); } if (mY < region.getTop()) { mY = region.getTop(); bounceOffTop(); } else if (mY > region.getBottom()) { mY = region.getBottom(); bounceOffBottom(); } mRegion = region; } public void setNow(long now) { mLastUpdate = now; } public boolean isCircleOverlapping(Ball otherBall) { final float dy = otherBall.mY - mY; final float dx = otherBall.mX - mX; final float distance = dy * dy + dx * dx; return (distance < ((2 * mRadiusPixels) * (2 *mRadiusPixels))) // avoid jittery collisions && !movingAwayFromEachother(this, otherBall); } private boolean movingAwayFromEachother(Ball ballA, Ball ballB) { double collA = Math.atan2(ballB.mY - ballA.mY, ballB.mX - ballA.mX); double collB = Math.atan2(ballA.mY - ballB.mY, ballA.mX - ballB.mX); double ax = Math.cos(ballA.mAngle - collA); double bx = Math.cos(ballB.mAngle - collB); return ax + bx < 0; } public void update(long now) { if (now <= mLastUpdate) return; // bounce when at walls if (mX <= mRegion.getLeft() + mRadiusPixels) { // we're at left wall mX = mRegion.getLeft() + mRadiusPixels; bounceOffLeft(); } else if (mY <= mRegion.getTop() + mRadiusPixels) { // at top wall mY = mRegion.getTop() + mRadiusPixels; bounceOffTop(); } else if (mX >= mRegion.getRight() - mRadiusPixels) { // at right wall mX = mRegion.getRight() - mRadiusPixels; bounceOffRight(); } else if (mY >= mRegion.getBottom() - mRadiusPixels) { // at bottom wall mY = mRegion.getBottom() - mRadiusPixels; bounceOffBottom(); } float delta = (now - mLastUpdate) * mPixelsPerSecond; delta = delta / 1000f; mX += (delta * Math.cos(mAngle)); mY += (delta * Math.sin(mAngle)); mLastUpdate = now; } private void bounceOffBottom() { if (mAngle < 0.5*Math.PI) { // going right mAngle = -mAngle; } else { // going left mAngle += (Math.PI - mAngle) * 2; } } private void bounceOffRight() { if (mAngle > 1.5*Math.PI) { // going up mAngle -= (mAngle - 1.5*Math.PI) * 2; } else { // going down mAngle += (.5*Math.PI - mAngle) * 2; } } private void bounceOffTop() { if (mAngle < 1.5 * Math.PI) { // going left mAngle -= (mAngle - Math.PI) * 2; } else { // going right mAngle += (2*Math.PI - mAngle) * 2; mAngle -= 2*Math.PI; } } private void bounceOffLeft() { if (mAngle < Math.PI) { // going down mAngle -= ((mAngle - (Math.PI / 2)) * 2); } else { // going up mAngle += (((1.5 * Math.PI) - mAngle) * 2); } } /** * Given that ball a and b have collided, adjust their angles to reflect their state * after the collision. * * This method works based on the conservation of energy and momentum in an elastic * collision. Because the balls have equal mass and speed, it ends up being that they * simply swap velocities along the axis of the collision, keeping the velocities tangent * to the collision constant. * * @param ballA The first ball in a collision * @param ballB The second ball in a collision */ public static void adjustForCollision(Ball ballA, Ball ballB) { final double collA = Math.atan2(ballB.mY - ballA.mY, ballB.mX - ballA.mX); final double collB = Math.atan2(ballA.mY - ballB.mY, ballA.mX - ballB.mX); final double ax = Math.cos(ballA.mAngle - collA); final double ay = Math.sin(ballA.mAngle - collA); final double bx = Math.cos(ballB.mAngle - collB); final double by = Math.cos(ballB.mAngle - collB); final double diffA = Math.atan2(ay, -bx); final double diffB = Math.atan2(by, -ax); ballA.mAngle = collA + diffA; ballB.mAngle = collB + diffB; } @Override public String toString() { return String.format( "Ball(x=%f, y=%f, angle=%f)", mX, mY, Math.toDegrees(mAngle)); } /** * A more readable way to create balls than using a 5 param * constructor of all numbers. */ public static class Builder { private long mNow = -1; private float mX = -1; private float mY = -1; private double mAngle = -1; private float mRadiusPixels = -1; private float mPixelsPerSecond = 45f; public Ball create() { if (mNow < 0) { throw new IllegalStateException("must set 'now'"); } if (mX < 0) { throw new IllegalStateException("X must be set"); } if (mY < 0) { throw new IllegalStateException("Y must be stet"); } if (mAngle < 0) { throw new IllegalStateException("angle must be set"); } if (mAngle > 2 * Math.PI) { throw new IllegalStateException("angle must be less that 2Pi"); } if (mRadiusPixels <= 0) { throw new IllegalStateException("radius must be set"); } return new Ball(mNow, mPixelsPerSecond, mX, mY, mAngle, mRadiusPixels); } public Builder setNow(long now) { mNow = now; return this; } public Builder setPixelsPerSecond(float pixelsPerSecond) { mPixelsPerSecond = pixelsPerSecond; return this; } public Builder setX(float x) { mX = x; return this; } public Builder setY(float y) { mY = y; return this; } public Builder setAngle(double angle) { mAngle = angle; return this; } public Builder setRadiusPixels(float pixels) { mRadiusPixels = pixels; return this; } } } Other Android examples (source code examples)Here is a short list of links related to this Android Ball.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.