I've been playing around with Slick2D for Java and I managed to get it to display maps and have my character sprite move around.
I've tried to implement a camera that follows the player so that the map scrolls. While the map is scrolling, that characters move speed is faster than it should be (possibly due to the camera srolling it as well as it moving with the keys)
I'm stumped on how to solve it though
The camera code is something i found on the slick forums, and modified slightly to draw each layer seperatly, modifying both drawmap methods to add the layer in. I would ask on the forums but they seem dead.
This is the camera code
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package engine;
/**
*
* #author Ceri
*/
import java.awt.geom.Point2D;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.geom.Shape;
import org.newdawn.slick.tiled.TiledMap;
public class Camera {
/**
* the map used for our scene
*/
protected TiledMap map;
/**
* the number of tiles in x-direction (width)
*/
protected int numTilesX;
/**
* the number of tiles in y-direction (height)
*/
protected int numTilesY;
/**
* the height of the map in pixel
*/
protected int mapHeight;
/**
* the width of the map in pixel
*/
protected int mapWidth;
/**
* the width of one tile of the map in pixel
*/
protected int tileWidth;
/**
* the height of one tile of the map in pixel
*/
protected int tileHeight;
/**
* the GameContainer, used for getting the size of the GameCanvas
*/
protected GameContainer gc;
/**
* the x-position of our "camera" in pixel
*/
protected float cameraX;
/**
* the y-position of our "camera" in pixel
*/
protected float cameraY;
protected Point2D.Float currentCenterPoint = new Point2D.Float(0, 0);
/**
* Create a new camera
*
* #param gc the GameContainer, used for getting the size of the GameCanvas
* #param map the TiledMap used for the current scene
*/
public Camera(GameContainer gc, TiledMap map) {
this.map = map;
this.numTilesX = map.getWidth();
this.numTilesY = map.getHeight();
this.tileWidth = map.getTileWidth();
this.tileHeight = map.getTileHeight();
this.mapWidth = this.numTilesX * this.tileWidth;
this.mapHeight = this.numTilesY * this.tileHeight;
this.gc = gc;
}
/**
* "locks" the camera on the given coordinates. The camera tries to keep the
* location in it's center.
*
* #param x the real x-coordinate (in pixel) which should be centered on the
* screen
* #param y the real y-coordinate (in pixel) which should be centered on the
* screen
* #return
*/
public Point2D.Float centerOn(float x, float y) {
//try to set the given position as center of the camera by default
cameraX = x - gc.getWidth() / 2;
cameraY = y - gc.getHeight() / 2;
//if the camera is at the right or left edge lock it to prevent a black bar
if (cameraX < 0) {
cameraX = 0;
}
if (cameraX + gc.getWidth() > mapWidth) {
cameraX = mapWidth - gc.getWidth();
}
//if the camera is at the top or bottom edge lock it to prevent a black bar
if (cameraY < 0) {
cameraY = 0;
}
if (cameraY + gc.getHeight() > mapHeight) {
cameraY = mapHeight - gc.getHeight();
}
currentCenterPoint.setLocation(cameraX, cameraY);
return currentCenterPoint;
}
/**
* "locks" the camera on the center of the given Rectangle. The camera tries
* to keep the location in it's center.
*
* #param x the x-coordinate (in pixel) of the top-left corner of the
* rectangle
* #param y the y-coordinate (in pixel) of the top-left corner of the
* rectangle
* #param height the height (in pixel) of the rectangle
* #param width the width (in pixel) of the rectangle
*/
public void centerOn(float x, float y, float height, float width) {
this.centerOn(x + width / 2, y + height / 2);
}
/**
* "locks the camera on the center of the given Shape. The camera tries to
* keep the location in it's center.
*
* #param shape the Shape which should be centered on the screen
*/
public void centerOn(Shape shape) {
this.centerOn(shape.getCenterX(), shape.getCenterY());
}
/**
* draws the part of the map which is currently focussed by the camera on
* the screen
*/
public void drawMap(int layer) {
this.drawMap(0, 0, layer);
}
/**
* draws the part of the map which is currently focussed by the camera on
* the screen.<br>
* You need to draw something over the offset, to prevent the edge of the
* map to be displayed below it<br>
* Has to be called before Camera.translateGraphics() !
*
* #param offsetX the x-coordinate (in pixel) where the camera should start
* drawing the map at
* #param offsetY the y-coordinate (in pixel) where the camera should start
* drawing the map at
*/
public void drawMap(int offsetX, int offsetY, int layer) {
//calculate the offset to the next tile (needed by TiledMap.render())
int tileOffsetX = (int) -(cameraX % tileWidth);
int tileOffsetY = (int) -(cameraY % tileHeight);
//calculate the index of the leftmost tile that is being displayed
int tileIndexX = (int) (cameraX / tileWidth);
int tileIndexY = (int) (cameraY / tileHeight);
//finally draw the section of the map on the screen
map.render(
tileOffsetX + offsetX,
tileOffsetY + offsetY,
tileIndexX,
tileIndexY,
(gc.getWidth() - tileOffsetX) / tileWidth + 1,
(gc.getHeight() - tileOffsetY) / tileHeight + 1, layer, false);
}
/**
* Translates the Graphics-context to the coordinates of the map - now
* everything can be drawn with it's NATURAL coordinates.
*/
public void translateGraphics() {
gc.getGraphics().translate(-cameraX, -cameraY);
}
/**
* Reverses the Graphics-translation of Camera.translatesGraphics(). Call
* this before drawing HUD-elements or the like
*/
public void untranslateGraphics() {
gc.getGraphics().translate(cameraX, cameraY);
}
}
and this is how its being called
In the engine class
public void render(GameContainer gc, Graphics g) throws SlickException {
camera = new Camera(gc, world.map);
camera.centerOn(player.getX(), player.getY());
camera.drawMap(0);
camera.drawMap(1);
player.draw();
camera.drawMap(2);
}
This is how the player class is
public Player(MapClass m) throws SlickException {
map = m;
Image[] movementUp = {new Image("Images/Player/u1.png"), new Image("Images/Player/u2.png"), new Image("Images/Player/u3.png"), new Image("Images/Player/u4.png")};
Image[] movementDown = {new Image("Images/Player/d1.png"), new Image("Images/Player/d2.png"), new Image("Images/Player/d3.png"), new Image("Images/Player/d4.png")};
Image[] movementLeft = {new Image("Images/Player/l1.png"), new Image("Images/Player/l2.png"), new Image("Images/Player/l3.png"), new Image("Images/Player/l4.png")};
Image[] movementRight = {new Image("Images/Player/r1.png"), new Image("Images/Player/r2.png"), new Image("Images/Player/r3.png"), new Image("Images/Player/r4.png")};
int[] duration = {100, 100, 100, 100};
up = new Animation(movementUp, duration, false);
down = new Animation(movementDown, duration, false);
left = new Animation(movementLeft, duration, false);
right = new Animation(movementRight, duration, false);
// Original orientation of the sprite. It will look right.
sprite = right;
}
public void update(GameContainer container, int delta) throws SlickException {
Input input = container.getInput();
if (input.isKeyDown(Input.KEY_UP)) {
sprite = up;
sprite.update(delta);
// The lower the delta the slowest the sprite will animate.
if (!map.isBlocked(x, y - delta * 0.1f))
y -= delta * 0.1f;
} else if (input.isKeyDown(Input.KEY_DOWN)) {
sprite = down;
sprite.update(delta);
if (!map.isBlocked(x, y + 16 + delta * 0.1f))
y += delta * 0.1f;
} else if (input.isKeyDown(Input.KEY_LEFT)) {
sprite = left;
sprite.update(delta);
if (!map.isBlocked(x - delta * 0.1f, y))
x -= delta * 0.1f;
} else if (input.isKeyDown(Input.KEY_RIGHT)) {
sprite = right;
sprite.update(delta);
if (!map.isBlocked(x + 16 + delta * 0.1f, y))
x += delta * 0.1f;
}
}
public void draw() {
sprite.draw(x, y);
}
Fixed it. Moved the map draw out of the camera class into the map class. used the camera x/y created in the camera class in the map and player class
Related
I am trying to touch and drag the card and update its position. Firstly, I want the card class to detect the touch drag event when I click on the card.
I have provided the code below for the card class and want help on how to detect touch event. I want to know what methods I should add and how to implement the x and y coordinates.
public class Card extends Sprite {
// /////////////////////////////////////////////////////////////////////////
// Properties:
// /////////////////////////////////////////////////////////////////////////
// Define the default card width and height
private static final int DEFAULT_CARD_WIDTH = 180;
private static final int DEFAULT_CARD_HEIGHT = 260;
// Define the common card base
private Bitmap mCardBase;
// Define the card portrait image
private Bitmap mCardPortrait;
// Define the card digit images
private Bitmap[] mCardDigits = new Bitmap[10];
// Define the offset locations and scaling for the card portrait
// card attack and card health values - all measured relative
// to the centre of the object as a percentage of object size
private Vector2 mAttackOffset = new Vector2(-0.68f, -0.84f);
private Vector2 mAttackScale = new Vector2(0.1f, 0.1f);
private Vector2 mHealthOffset = new Vector2(0.72f, -0.84f);
private Vector2 mHealthScale = new Vector2(0.1f, 0.1f);
private Vector2 mPortraitOffset = new Vector2(0.0f, 0.3f);
private Vector2 mPortraitScale = new Vector2(0.55f, 0.55f);
// Define the health and attack values
private int mAttack;
private int mHealth;
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a new platform.
*
* #param x Centre y location of the platform
* #param y Centre x location of the platform
* #param gameScreen Gamescreen to which this platform belongs
*/
public Card(float x, float y, GameScreen gameScreen) {
super(x, y, DEFAULT_CARD_WIDTH, DEFAULT_CARD_HEIGHT, null, gameScreen);
AssetManager assetManager = gameScreen.getGame().getAssetManager();
// Store the common card base image
mCardBase = assetManager.getBitmap("CardBackground");
// Store the card portrait image
mCardPortrait = assetManager.getBitmap("CardPortrait");
// Store each of the damage/health digits
for(int digit = 0; digit <= 9; digit++)
mCardDigits[digit] = assetManager.getBitmap(String.valueOf(digit));
// Set default attack and health values
mAttack = 1;
mHealth = 2;
}
// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////
/**
* Draw the game platform
*
* #param elapsedTime Elapsed time information
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D,
LayerViewport layerViewport, ScreenViewport screenViewport) {
// Draw the portrait
drawBitmap(mCardPortrait, mPortraitOffset, mPortraitScale,
graphics2D, layerViewport, screenViewport);
// Draw the card base background
mBitmap = mCardBase;
super.draw(elapsedTime, graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mAttack], mAttackOffset, mAttackScale,
graphics2D, layerViewport, screenViewport);
// Draw the attack value
drawBitmap(mCardDigits[mHealth], mHealthOffset, mHealthScale,
graphics2D, layerViewport, screenViewport);
}
private BoundingBox bound = new BoundingBox();
/**
* Method to draw out a specified bitmap using a specific offset (relative to the
* position of this game object) and scaling (relative to the size of this game
* object).
*
* #param bitmap Bitmap to draw
* #param offset Offset vector
* #param scale Scaling vector
* #param graphics2D Graphics instance
* #param layerViewport Game layer viewport
* #param screenViewport Screen viewport
*/
private void drawBitmap(Bitmap bitmap, Vector2 offset, Vector2 scale,
IGraphics2D graphics2D, LayerViewport layerViewport, ScreenViewport screenViewport) {
// // Calculate a game layer bound for the bitmap to be drawn
// bound.set(position.x + mBound.halfWidth * offset.x,
// position.y + mBound.halfHeight * offset.y,
// mBound.halfWidth * scale.x,
// mBound.halfHeight * scale.y);
// Calculate the center position of the rotated offset point.
double rotation = Math.toRadians(-this.orientation);
float diffX = mBound.halfWidth * offset.x;
float diffY = mBound.halfHeight * offset.y;
float rotatedX = (float)(Math.cos(rotation) * diffX - Math.sin(rotation) * diffY + position.x);
float rotatedY = (float)(Math.sin(rotation) * diffX + Math.cos(rotation) * diffY + position.y);
// Calculate a game layer bound for the bitmap to be drawn
bound.set(rotatedX, rotatedY,
mBound.halfWidth * scale.x, mBound.halfHeight * scale.y);
// Draw out the specified bitmap using the calculated bound.
// The following code is based on the Sprite's draw method.
if (GraphicsHelper.getSourceAndScreenRect(
bound, bitmap, layerViewport, screenViewport, drawSourceRect, drawScreenRect)) {
// Build an appropriate transformation matrix
drawMatrix.reset();
float scaleX = (float) drawScreenRect.width() / (float) drawSourceRect.width();
float scaleY = (float) drawScreenRect.height() / (float) drawSourceRect.height();
drawMatrix.postScale(scaleX, scaleY);
drawMatrix.postRotate(orientation, scaleX * bitmap.getWidth()
/ 2.0f, scaleY * bitmap.getHeight() / 2.0f);
drawMatrix.postTranslate(drawScreenRect.left, drawScreenRect.top);
// Draw the bitmap
graphics2D.drawBitmap(bitmap, drawMatrix, null);
}
}
}
I want to place the text section CONTROLS to the right instead of it just being listed, I'm a newbie and I'm using a source code from a website that publishes Java game codes. The Y coordinate exists but I can't seem to find the X coordinate which will help me to position the text section. This is the code for my side panel class:
/**
* The number of rows and columns in the preview window. Set to
* 5 because we can show any piece with some sort of padding.
*/
private static final int TILE_COUNT = 5;
/**
* The center x of the next piece preview box.
*/
private static final int SQUARE_CENTER_X = 130;
/**
* The center y of the next piece preview box.
*/
private static final int SQUARE_CENTER_Y = 65;
/**
* The size of the next piece preview box.
*/
private static final int SQUARE_SIZE = (TILE_SIZE * TILE_COUNT >> 1);
/**
* The number of pixels used on a small insets (generally used for categories).
*/
private static final int SMALL_INSET = 20;
/**
* The number of pixels used on a large insets.
*/
private static final int LARGE_INSET = 30;
/**
* The y coordinate of the stats category.
*/
private static final int STATS_INSET = 100;
/**
* The y coordinate of the controls category.
*/
private static final int CONTROLS_INSET = 175;
/**
* The number of pixels to offset between each string.
*/
private static final int TEXT_STRIDE = 25;
/**
* The small font.
*/
private static final Font SMALL_FONT = new Font("Arial", Font.BOLD, 11);
/**
* The large font.
*/
private static final Font LARGE_FONT = new Font("Arial", Font.BOLD, 13);
/**
* The color to draw the text and preview box in.
*/
private static final Color DRAW_COLOR = new Color(128, 192, 128);
/**
* The Tetris instance.
*/
private Tetris tetris;
/**
* Creates a new SidePanel and sets it's display properties.
* #param tetris The Tetris instance to use.
*/
public SidePanel(Tetris tetris) {
this.tetris = tetris;
setPreferredSize(new Dimension(200, BoardPanel.PANEL_HEIGHT));
setBackground(Color.BLACK);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
//Set the color for drawing.
g.setColor(Color.WHITE);
/*
* This variable stores the current y coordinate of the string.
* This way we can re-order, add, or remove new strings if necessary
* without needing to change the other strings.
*/
int offset;
/*
* Draw the "Stats" category.
*/
g.setFont(LARGE_FONT);
g.drawString("Stats", SMALL_INSET, offset = STATS_INSET);
g.setFont(SMALL_FONT);
g.drawString("Level: " + tetris.getLevel(), LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("Score: " + tetris.getScore(), LARGE_INSET, offset += TEXT_STRIDE);
/*
* Draw the "Controls" category.
*/
g.setFont(LARGE_FONT);
g.drawString("Controls", SMALL_INSET, offset = CONTROLS_INSET);
g.setFont(SMALL_FONT);
g.drawString(" Left- Move Left", LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("Right - Move Right", LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("Z - Rotate Anticlockwise", LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("X - Rotate Clockwise", LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("Down - Drop", LARGE_INSET, offset += TEXT_STRIDE);
g.drawString("P - Pause Game", LARGE_INSET, offset += TEXT_STRIDE);
/*
* Draw the next piece preview box.
*/
g.setFont(LARGE_FONT);
g.drawString("Next Piece:", SMALL_INSET, 70);
g.drawRect(SQUARE_CENTER_X - SQUARE_SIZE, SQUARE_CENTER_Y - SQUARE_SIZE, SQUARE_SIZE * 2, SQUARE_SIZE * 2);
/*
* Draw a preview of the next piece that will be spawned. The code is pretty much
* identical to the drawing code on the board, just smaller and centered, rather
* than constrained to a grid.
*/
TileType type = tetris.getNextPieceType();
if(!tetris.isGameOver() && type != null) {
/*
* Get the size properties of the current piece.
*/
int cols = type.getCols();
int rows = type.getRows();
int dimension = type.getDimension();
/*
* Calculate the top left corner (origin) of the piece.
*/
int startX = (SQUARE_CENTER_X - (cols * TILE_SIZE / 2));
int startY = (SQUARE_CENTER_Y - (rows * TILE_SIZE / 2));
/*
* Get the insets for the preview. The default
* rotation is used for the preview, so we just use 0.
*/
int top = type.getTopInset(0);
int left = type.getLeftInset(0);
/*
* Loop through the piece and draw it's tiles onto the preview.
*/
for(int row = 0; row < dimension; row++) {
for(int col = 0; col < dimension; col++) {
if(type.isTile(col, row, 0)) {
drawTile(type, startX + ((col - left) * TILE_SIZE), startY + ((row - top) * TILE_SIZE), g);
}
}
}
}
}
/**
* Draws a tile onto the preview window.
* #param type The type of tile to draw.
* #param x The x coordinate of the tile.
* #param y The y coordinate of the tile.
* #param g The graphics object.
*/
private void drawTile(TileType type, int x, int y, Graphics g) {
/*
* Fill the entire tile with the base color.
*/
g.setColor(type.getBaseColor());
g.fillRect(x, y, TILE_SIZE, TILE_SIZE);
/*
* Fill the bottom and right edges of the tile with the dark shading color.
*/
g.setColor(type.getDarkColor());
g.fillRect(x, y + TILE_SIZE - SHADE_WIDTH, TILE_SIZE, SHADE_WIDTH);
g.fillRect(x + TILE_SIZE - SHADE_WIDTH, y, SHADE_WIDTH, TILE_SIZE);
/*
* Fill the top and left edges with the light shading. We draw a single line
* for each row or column rather than a rectangle so that we can draw a nice
* looking diagonal where the light and dark shading meet.
*/
g.setColor(type.getLightColor());
for(int i = 0; i < SHADE_WIDTH; i++) {
g.drawLine(x, y + i, x + TILE_SIZE - i - 1, y + i);
g.drawLine(x + i, y, x + i, y + TILE_SIZE - i - 1);
}
}
}
I was looking Cropper and image crooper open source App by Edmodo, it does a pretty good job except instead of Image cropping I want to use it to scale my images, my Logic Was everytime the cropper Window/View changes in size I would change the height and width of my imageview to match the Crop view but everytime I do this, nothing happens, but looking at LogCat it seems my height and width do change except the imageview still looks the same even after changes below is the Class, my changes are commented in the onSizeChanged()
/*
* Copyright 2013, Edmodo, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License.
* You may obtain a copy of the License in the LICENSE file, or 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.theartofdev.edmodo.cropper.cropwindow;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.theartofdev.edmodo.cropper.CropImageView;
import com.theartofdev.edmodo.cropper.cropwindow.edge.Edge;
import com.theartofdev.edmodo.cropper.cropwindow.handle.Handle;
import com.theartofdev.edmodo.cropper.util.AspectRatioUtil;
import com.theartofdev.edmodo.cropper.util.HandleUtil;
import com.theartofdev.edmodo.cropper.util.PaintUtil;
/**
* A custom View representing the crop window and the shaded background outside the crop window.
*/
public class CropOverlayView extends View {
//region: Fields and Consts
private static final int SNAP_RADIUS_DP = 6;
private static final float DEFAULT_SHOW_GUIDELINES_LIMIT = 100;
// Gets default values from PaintUtil, sets a bunch of values such that the
// corners will draw correctly
private static final float DEFAULT_CORNER_THICKNESS_DP = PaintUtil.getCornerThickness();
private static final float DEFAULT_LINE_THICKNESS_DP = PaintUtil.getLineThickness();
private static final float DEFAULT_CORNER_OFFSET_DP = (DEFAULT_CORNER_THICKNESS_DP / 2) - (DEFAULT_LINE_THICKNESS_DP / 2);
private static final float DEFAULT_CORNER_EXTENSION_DP = DEFAULT_CORNER_THICKNESS_DP / 2
+ DEFAULT_CORNER_OFFSET_DP;
private static final float DEFAULT_CORNER_LENGTH_DP = 20;
private static final int GUIDELINES_ON_TOUCH = 1;
private static final int GUIDELINES_ON = 2;
private static RectF mRectF = new RectF();
/**
* The Paint used to draw the white rectangle around the crop area.
*/
private Paint mBorderPaint;
/**
* The Paint used to draw the guidelines within the crop area when pressed.
*/
private Paint mGuidelinePaint;
/**
* The Paint used to draw the corners of the Border
*/
private Paint mCornerPaint;
/**
* The Paint used to darken the surrounding areas outside the crop area.
*/
private Paint mBackgroundPaint;
/**
* The bounding box around the Bitmap that we are cropping.
*/
private Rect mBitmapRect;
// The radius of the touch zone (in pixels) around a given Handle.
private float mHandleRadius;
// An edge of the crop window will snap to the corresponding edge of a
// specified bounding box when the crop window edge is less than or equal to
// this distance (in pixels) away from the bounding box edge.
private float mSnapRadius;
// Holds the x and y offset between the exact touch location and the exact
// handle location that is activated. There may be an offset because we
// allow for some leeway (specified by mHandleRadius) in activating a
// handle. However, we want to maintain these offset values while the handle
// is being dragged so that the handle doesn't jump.
private Pair<Float, Float> mTouchOffset;
// The Handle that is currently pressed; null if no Handle is pressed.
private Handle mPressedHandle;
// Flag indicating if the crop area should always be a certain aspect ratio
// (indicated by mTargetAspectRatio).
private boolean mFixAspectRatio = CropImageView.DEFAULT_FIXED_ASPECT_RATIO;
// Floats to save the current aspect ratio of the image
private int mAspectRatioX = CropImageView.DEFAULT_ASPECT_RATIO_X;
private int mAspectRatioY = CropImageView.DEFAULT_ASPECT_RATIO_Y;
// The aspect ratio that the crop area should maintain; this variable is
// only used when mMaintainAspectRatio is true.
private float mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
/**
* Instance variables for customizable attributes
*/
private int mGuidelines;
/**
* The shape of the cropping area - rectangle/circular.
*/
private CropImageView.CropShape mCropShape;
// Whether the Crop View has been initialized for the first time
private boolean initializedCropWindow = false;
// Instance variables for the corner values
private float mCornerExtension;
private float mCornerOffset;
private float mCornerLength;
//endregion
public CropOverlayView(Context context) {
super(context);
init(context);
}
public CropOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* Informs the CropOverlayView of the image's position relative to the
* ImageView. This is necessary to call in order to draw the crop window.
*
* #param bitmapRect the image's bounding box
*/
public void setBitmapRect(Rect bitmapRect) {
mBitmapRect = bitmapRect;
initCropWindow(mBitmapRect);
}
/**
* Resets the crop overlay view.
*/
public void resetCropOverlayView() {
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
/**
* The shape of the cropping area - rectangle/circular.
*/
public void setCropShape(CropImageView.CropShape cropShape) {
mCropShape = cropShape;
invalidate();
}
/**
* Sets the guidelines for the CropOverlayView to be either on, off, or to
* show when resizing the application.
*
* #param guidelines Integer that signals whether the guidelines should be
* on, off, or only showing when resizing.
*/
public void setGuidelines(int guidelines) {
if (guidelines < 0 || guidelines > 2)
throw new IllegalArgumentException("Guideline value must be set between 0 and 2. See documentation.");
else {
mGuidelines = guidelines;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets whether the aspect ratio is fixed or not; true fixes the aspect
* ratio, while false allows it to be changed.
*
* #param fixAspectRatio Boolean that signals whether the aspect ratio
* should be maintained.
*/
public void setFixedAspectRatio(boolean fixAspectRatio) {
mFixAspectRatio = fixAspectRatio;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
/**
* Sets the X value of the aspect ratio; is defaulted to 1.
*
* #param aspectRatioX int that specifies the new X value of the aspect
* ratio
*/
public void setAspectRatioX(int aspectRatioX) {
if (aspectRatioX <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioX = aspectRatioX;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets the Y value of the aspect ratio; is defaulted to 1.
*
* #param aspectRatioY int that specifies the new Y value of the aspect
* ratio
*/
public void setAspectRatioY(int aspectRatioY) {
if (aspectRatioY <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioY = aspectRatioY;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
if (initializedCropWindow) {
initCropWindow(mBitmapRect);
invalidate();
}
}
}
/**
* Sets all initial values, but does not call initCropWindow to reset the
* views. Used once at the very start to initialize the attributes.
*
* #param guidelines Integer that signals whether the guidelines should be
* on, off, or only showing when resizing.
* #param fixAspectRatio Boolean that signals whether the aspect ratio
* should be maintained.
* #param aspectRatioX float that specifies the new X value of the aspect
* ratio
* #param aspectRatioY float that specifies the new Y value of the aspect
* ratio
*/
public void setInitialAttributeValues(int guidelines, boolean fixAspectRatio, int aspectRatioX, int aspectRatioY) {
if (guidelines < 0 || guidelines > 2)
throw new IllegalArgumentException("Guideline value must be set between 0 and 2. See documentation.");
else
mGuidelines = guidelines;
mFixAspectRatio = fixAspectRatio;
if (aspectRatioX <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioX = aspectRatioX;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
}
if (aspectRatioY <= 0)
throw new IllegalArgumentException("Cannot set aspect ratio value to a number less than or equal to 0.");
else {
mAspectRatioY = aspectRatioY;
mTargetAspectRatio = ((float) mAspectRatioX) / mAspectRatioY;
}
}
//region: Private methods
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Initialize the crop window here because we need the size of the view
// to have been determined.
//my changes to update imageview height and width
CropImageView.mImageView.getLayoutParams().height = h;
CropImageView.mImageView.getLayoutParams().width = w;
CropImageView.mImageView.requestLayout();
CropImageView.mImageView.invalidate();
initCropWindow(mBitmapRect);
// CropImageView.mImageView.invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw translucent background for the cropped area.
drawBackground(canvas, mBitmapRect);
if (showGuidelines()) {
// Determines whether guidelines should be drawn or not
if (mGuidelines == GUIDELINES_ON) {
drawRuleOfThirdsGuidelines(canvas);
} else if (mGuidelines == GUIDELINES_ON_TOUCH) {
// Draw only when resizing
if (mPressedHandle != null)
drawRuleOfThirdsGuidelines(canvas);
}
}
float w = mBorderPaint.getStrokeWidth();
float l = Edge.LEFT.getCoordinate() + w;
float t = Edge.TOP.getCoordinate() + w;
float r = Edge.RIGHT.getCoordinate() - w;
float b = Edge.BOTTOM.getCoordinate() - w;
if (mCropShape == CropImageView.CropShape.RECTANGLE) {
// Draw rectangle crop window border.
canvas.drawRect(l, t, r, b, mBorderPaint);
drawCorners(canvas);
} else {
// Draw circular crop window border
mRectF.set(l, t, r, b);
canvas.drawOval(mRectF, mBorderPaint);
}
}
#Override
public boolean onTouchEvent(#SuppressWarnings("NullableProblems") MotionEvent event) {
// If this View is not enabled, don't allow for touch interactions.
if (!isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
onActionDown(event.getX(), event.getY());
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
getParent().requestDisallowInterceptTouchEvent(false);
onActionUp();
return true;
case MotionEvent.ACTION_MOVE:
onActionMove(event.getX(), event.getY());
getParent().requestDisallowInterceptTouchEvent(true);
return true;
default:
return false;
}
}
private void init(Context context) {
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
mHandleRadius = HandleUtil.getTargetRadius(context);
mSnapRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
SNAP_RADIUS_DP,
displayMetrics);
mBorderPaint = PaintUtil.newBorderPaint(context);
mGuidelinePaint = PaintUtil.newGuidelinePaint();
mBackgroundPaint = PaintUtil.newBackgroundPaint(context);
mCornerPaint = PaintUtil.newCornerPaint(context);
// Sets the values for the corner sizes
mCornerOffset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_OFFSET_DP,
displayMetrics);
mCornerExtension = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_EXTENSION_DP,
displayMetrics);
mCornerLength = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
DEFAULT_CORNER_LENGTH_DP,
displayMetrics);
// Sets guidelines to default until specified otherwise
mGuidelines = CropImageView.DEFAULT_GUIDELINES;
}
/**
* Set the initial crop window size and position. This is dependent on the
* size and position of the image being cropped.
*
* #param bitmapRect the bounding box around the image being cropped
*/
private void initCropWindow(Rect bitmapRect) {
if (bitmapRect.width() == 0 || bitmapRect.height() == 0) {
return;
}
// Tells the attribute functions the crop window has already been
// initialized
if (!initializedCropWindow) {
initializedCropWindow = true;
}
if (mFixAspectRatio
&& (bitmapRect.left != 0 || bitmapRect.right != 0
|| bitmapRect.top != 0 || bitmapRect.bottom != 0)) {
// If the image aspect ratio is wider than the crop aspect ratio,
// then the image height is the determining initial length. Else,
// vice-versa.
if (AspectRatioUtil.calculateAspectRatio(bitmapRect) > mTargetAspectRatio) {
Edge.TOP.setCoordinate(bitmapRect.top);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom);
final float centerX = getWidth() / 2f;
//dirty fix for wrong crop overlay aspect ratio when using fixed aspect ratio
mTargetAspectRatio = (float) mAspectRatioX / mAspectRatioY;
// Limits the aspect ratio to no less than 40 wide or 40 tall
final float cropWidth = Math.max(Edge.MIN_CROP_LENGTH_PX,
AspectRatioUtil.calculateWidth(Edge.TOP.getCoordinate(),
Edge.BOTTOM.getCoordinate(),
mTargetAspectRatio));
// Create new TargetAspectRatio if the original one does not fit
// the screen
if (cropWidth == Edge.MIN_CROP_LENGTH_PX) {
mTargetAspectRatio = (Edge.MIN_CROP_LENGTH_PX) / (Edge.BOTTOM.getCoordinate() - Edge.TOP.getCoordinate());
}
final float halfCropWidth = cropWidth / 2f;
Edge.LEFT.setCoordinate(centerX - halfCropWidth);
Edge.RIGHT.setCoordinate(centerX + halfCropWidth);
} else {
Edge.LEFT.setCoordinate(bitmapRect.left);
Edge.RIGHT.setCoordinate(bitmapRect.right);
final float centerY = getHeight() / 2f;
// Limits the aspect ratio to no less than 40 wide or 40 tall
final float cropHeight = Math.max(Edge.MIN_CROP_LENGTH_PX,
AspectRatioUtil.calculateHeight(Edge.LEFT.getCoordinate(),
Edge.RIGHT.getCoordinate(),
mTargetAspectRatio));
// Create new TargetAspectRatio if the original one does not fit
// the screen
if (cropHeight == Edge.MIN_CROP_LENGTH_PX) {
mTargetAspectRatio = (Edge.RIGHT.getCoordinate() - Edge.LEFT.getCoordinate()) / Edge.MIN_CROP_LENGTH_PX;
}
final float halfCropHeight = cropHeight / 2f;
Edge.TOP.setCoordinate(centerY - halfCropHeight);
Edge.BOTTOM.setCoordinate(centerY + halfCropHeight);
}
} else { // ... do not fix aspect ratio...
// Initialize crop window to have 10% padding w/ respect to image.
final float horizontalPadding = 0.1f * bitmapRect.width();
final float verticalPadding = 0.1f * bitmapRect.height();
Edge.LEFT.setCoordinate(bitmapRect.left + horizontalPadding);
Edge.TOP.setCoordinate(bitmapRect.top + verticalPadding);
Edge.RIGHT.setCoordinate(bitmapRect.right - horizontalPadding);
Edge.BOTTOM.setCoordinate(bitmapRect.bottom - verticalPadding);
}
}
/**
* Indicates whether the crop window is small enough that the guidelines
* should be shown. Public because this function is also used to determine
* if the center handle should be focused.
*
* #return boolean Whether the guidelines should be shown or not
*/
public static boolean showGuidelines() {
if ((Math.abs(Edge.LEFT.getCoordinate() - Edge.RIGHT.getCoordinate()) < DEFAULT_SHOW_GUIDELINES_LIMIT)
|| (Math.abs(Edge.TOP.getCoordinate() - Edge.BOTTOM.getCoordinate()) < DEFAULT_SHOW_GUIDELINES_LIMIT)) {
return false;
} else {
return true;
}
}
private void drawRuleOfThirdsGuidelines(Canvas canvas) {
float w = mBorderPaint.getStrokeWidth();
float l = Edge.LEFT.getCoordinate() + w;
float t = Edge.TOP.getCoordinate() + w;
float r = Edge.RIGHT.getCoordinate() - w;
float b = Edge.BOTTOM.getCoordinate() - w;
if (mCropShape == CropImageView.CropShape.OVAL) {
l += 15 * mGuidelinePaint.getStrokeWidth();
t += 15 * mGuidelinePaint.getStrokeWidth();
r -= 15 * mGuidelinePaint.getStrokeWidth();
b -= 15 * mGuidelinePaint.getStrokeWidth();
}
// Draw vertical guidelines.
final float oneThirdCropWidth = Edge.getWidth() / 3;
final float x1 = l + oneThirdCropWidth;
canvas.drawLine(x1, t, x1, b, mGuidelinePaint);
final float x2 = r - oneThirdCropWidth;
canvas.drawLine(x2, t, x2, b, mGuidelinePaint);
// Draw horizontal guidelines.
final float oneThirdCropHeight = Edge.getHeight() / 3;
final float y1 = t + oneThirdCropHeight;
canvas.drawLine(l, y1, r, y1, mGuidelinePaint);
final float y2 = b - oneThirdCropHeight;
canvas.drawLine(l, y2, r, y2, mGuidelinePaint);
}
private void drawBackground(Canvas canvas, Rect bitmapRect) {
final float l = Edge.LEFT.getCoordinate();
final float t = Edge.TOP.getCoordinate();
final float r = Edge.RIGHT.getCoordinate();
final float b = Edge.BOTTOM.getCoordinate();
if (mCropShape == CropImageView.CropShape.RECTANGLE) {
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, t, mBackgroundPaint);
canvas.drawRect(bitmapRect.left, b, bitmapRect.right, bitmapRect.bottom, mBackgroundPaint);
canvas.drawRect(bitmapRect.left, t, l, b, mBackgroundPaint);
canvas.drawRect(r, t, bitmapRect.right, b, mBackgroundPaint);
} else {
Path circleSelectionPath = new Path();
mRectF.set(l, t, r, b);
circleSelectionPath.addOval(mRectF, Path.Direction.CW);
canvas.clipPath(circleSelectionPath, Region.Op.XOR);
canvas.drawRect(bitmapRect.left, bitmapRect.top, bitmapRect.right, bitmapRect.bottom, mBackgroundPaint);
canvas.restore();
}
}
private void drawCorners(Canvas canvas) {
float w = mBorderPaint.getStrokeWidth();
final float l = Edge.LEFT.getCoordinate() + w;
final float t = Edge.TOP.getCoordinate() + w;
final float r = Edge.RIGHT.getCoordinate() - w;
final float b = Edge.BOTTOM.getCoordinate() - w;
// Top left
canvas.drawLine(l - mCornerOffset, t - mCornerExtension, l - mCornerOffset, t + mCornerLength, mCornerPaint);
canvas.drawLine(l, t - mCornerOffset, l + mCornerLength, t - mCornerOffset, mCornerPaint);
// Top right
canvas.drawLine(r + mCornerOffset, t - mCornerExtension, r + mCornerOffset, t + mCornerLength, mCornerPaint);
canvas.drawLine(r, t - mCornerOffset, r - mCornerLength, t - mCornerOffset, mCornerPaint);
// Bottom left
canvas.drawLine(l - mCornerOffset, b + mCornerExtension, l - mCornerOffset, b - mCornerLength, mCornerPaint);
canvas.drawLine(l, b + mCornerOffset, l + mCornerLength, b + mCornerOffset, mCornerPaint);
// Bottom left
canvas.drawLine(r + mCornerOffset, b + mCornerExtension, r + mCornerOffset, b - mCornerLength, mCornerPaint);
canvas.drawLine(r, b + mCornerOffset, r - mCornerLength, b + mCornerOffset, mCornerPaint);
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_DOWN} event.
*
* #param x the x-coordinate of the down action
* #param y the y-coordinate of the down action
*/
private void onActionDown(float x, float y) {
final float left = Edge.LEFT.getCoordinate();
final float top = Edge.TOP.getCoordinate();
final float right = Edge.RIGHT.getCoordinate();
final float bottom = Edge.BOTTOM.getCoordinate();
mPressedHandle = HandleUtil.getPressedHandle(x, y, left, top, right, bottom, mHandleRadius);
if (mPressedHandle == null) {
return;
}
// Calculate the offset of the touch point from the precise location
// of the handle. Save these values in a member variable since we want
// to maintain this offset as we drag the handle.
mTouchOffset = HandleUtil.getOffset(mPressedHandle, x, y, left, top, right, bottom);
invalidate();
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_UP} or
* {#link android.view.MotionEvent#ACTION_CANCEL} event.
*/
private void onActionUp() {
if (mPressedHandle == null) {
return;
}
mPressedHandle = null;
invalidate();
}
/**
* Handles a {#link android.view.MotionEvent#ACTION_MOVE} event.
*
* #param x the x-coordinate of the move event
* #param y the y-coordinate of the move event
*/
private void onActionMove(float x, float y) {
if (mPressedHandle == null) {
return;
}
// Adjust the coordinates for the finger position's offset (i.e. the
// distance from the initial touch to the precise handle location).
// We want to maintain the initial touch's distance to the pressed
// handle so that the crop window size does not "jump".
x += mTouchOffset.first;
y += mTouchOffset.second;
// Calculate the new crop window size/position.
if (mFixAspectRatio) {
mPressedHandle.updateCropWindow(x, y, mTargetAspectRatio, mBitmapRect, mSnapRadius);
} else {
mPressedHandle.updateCropWindow(x, y, mBitmapRect, mSnapRadius);
}
invalidate();
}
//endregion
}
You could just try using Picasso. It does all the scaling for you in the background after you put it into your Gradle build. Picasso
I have tried implementing a smooth Camera zoom method in libgdx, in which he method zoom interpolates between two vlaues of zoom amount i.e initial * final.
The problem howevever is that the camera snaps fast into position when zoomed or moved. How can I be able to create a smooth camera class and implement zoom without having a jumpy scrren?
private void panZoom(Vector3 spanCord, TweenManager tweenManager, GameWorld world)
{
/*
* Example Tween-Sequence: Zoom to 120%, Pan to point of interest #1 (0, -50), Wait 1 second, Pan back to the
* starting position, Zoom back to the initial value
*/
Timeline.createSequence()
.beginParallel()
.push(Tween.to(this, OrthographicCameraAccessor.POSITION, 3.5f).target(spanCord.x,spanCord.y,spanCord.z).ease(Elastic.OUT))
.push(Tween.to(this, OrthographicCameraAccessor.ZOOM, 3.5f).target(1.20f).ease(Elastic.OUT))
.end()
.beginParallel()
.push(Tween.to(this, OrthographicCameraAccessor.POSITION, 2.8f).target(world.getYoyo().position.x,0,0).ease(Bounce.INOUT))
.push(Tween.to(this, OrthographicCameraAccessor.ZOOM, 3.0f).target(1).ease(Bounce.INOUT))
.end()
.start(tweenManager);
}
public float calcZoom(float initialDistance, float distance)
{
float nextZoom;
if(initialDistance < distance)
{
float ratio = (initialDistance/distance)/10;
nextZoom = zoomIn(ratio);
}
else
{
float ratio = (distance/initialDistance)/10;
nextZoom = zoomOut(ratio);
}
return nextZoom;
}
public class OrthographicCameraAccessor implements TweenAccessor<MyOrthographicCamera> {
/** Tween position */
public static final int POSITION = 1;
/** Tween zoom */
public static final int ZOOM = 2;
/**
* #param camera
* camera to get values from
* #param tweentype
* type of tween (Position or Zoom)
* #param returnValues
* out parameter with the requested values
*/
#Override
public int getValues(MyOrthographicCamera camera, int tweenType, float[] returnValues)
{
switch (tweenType)
{
case POSITION:
returnValues[0] = camera.position.x;
returnValues[1] = camera.position.y;
returnValues[2] = camera.position.z;
return 3;
case ZOOM:
returnValues[0] = camera.zoom;
return 1;
default:
return 0;
}
}
/**
* #param camera
* camera whose some values are going to be set
* #param tweenType
* Position or Zoom
* #param newValues
* array containing the new values to configure the camera
*/
#Override
public void setValues(MyOrthographicCamera camera, int tweenType, float[] newValues)
{
switch (tweenType)
{
case POSITION:
camera.setPosition(newValues[0], 0);
camera.update();
break;
case ZOOM:
camera.setZoom(newValues[0]);
camera.update();
Gdx.app.log("CAMERA", "Clamped Zoom" +camera.zoom);
break;
default:
break;
}
}
The context behind the this question is that I am part of a small group working to create a small game on Android. The game we have chosen to develop is a top-down 2D shooter where the player controls a spaceship that must defend itself from alien creatures that move towards the player from the top of the gamescreen, similar to space invader, but different in that they spawn in random patterns.
So far, we have been able to get the game running, have the player and enemies to spawn and move in the right direction, as well as the player's weapon which is labelled as "projectile" (although it currently just spawns on its own and not on the player command). The issue we have though is that we are trying to set up a collision event in the code between the projectile and the enemies so that when they meet, the enemy and bullet both disappear.
However, what we have currently written in the code does not seem to make this work, so I was wondering if perhaps anyone else could see where we are going wrong. I've listed the relevant classes of code below. We would greatly appreciate any help anyone can give.
Collision Detector class( Holds the behavoir for collision detection for all entities in the game).
package uk.ac.qub.eeecs.gage.util;
import uk.ac.qub.eeecs.gage.world.GameObject;
/**
* Collision Detector Helper Library
*
* #version 1.0
*/
public class CollisionDetector {
/**
* Type of collision
*/
public enum CollisionType {
None, Top, Bottom, Left, Right, destroy //for the platform game, not yours
};
/**
* Determine if the two specified bounding boxes are in collision
*
* #param one
* First bounding box
* #param two
* Second bounding box
* #return boolean true if the boxes overlap, false otherwise
*/
//is relevant to your game
public static boolean isCollision(BoundingBox one, BoundingBox two) {
return (one.x - one.halfWidth < two.x + two.halfWidth
&& one.x + one.halfWidth > two.x - two.halfWidth
&& one.y - one.halfHeight < two.y + two.halfHeight && one.y
+ one.halfHeight > two.y - two.halfHeight);
//Do we add the code to set the enemies to disappear/destroy here or in the
//individual classes such as the player?
}
/**
* Determine the type of collision between the two bounding boxes.
* CollisionType.None is returned if there are no collisions.
*
* #param one
* First bounding box
* #param two
* Second bounding box
* #return Collision type
*/
AISpaceship.java class (class used for emeny alien entities and their properties).
package uk.ac.qub.eeecs.game.spaceDemo;
import uk.ac.qub.eeecs.gage.ai.SteeringBehaviours;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.util.CollisionDetector;
import uk.ac.qub.eeecs.gage.util.Vector2;
import uk.ac.qub.eeecs.gage.util.CollisionDetector.CollisionType;
import uk.ac.qub.eeecs.gage.world.GameObject;
import uk.ac.qub.eeecs.gage.world.Sprite;
/**
* AI controlled spaceship
*
* #version 1.0
*/
public class AISpaceship extends Sprite {
// /////////////////////////////////////////////////////////////////////////
// Properties
// /////////////////////////////////////////////////////////////////////////
/**
* AI control behaviour
*/
/**
* Acceleration with which the spaceship can move along
* the x-axis
*/
private float RUN_ACCELERATION = 150.0f;
public enum ShipBehaviour {
Turret, Seeker
}
private boolean visible;
private ShipBehaviour mShipBehaviour;
/**
* Distance at which the spaceship should avoid other game objects
*/
private float separateThresholdShip = 75.0f;
private float separateThresholdAsteroid = 125.0f;
/**
* Accumulators used to build up the net steering outcome
*/
private Vector2 accAccumulator = new Vector2();
private Vector2 accComponent = new Vector2();
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a AI controlled spaceship
*
* #param startX
* x location of the AI spaceship
* #param startY
* y location of the AI spaceship
* #param shipBehaviour
* Steering behaviour to be used by the AI ship
* #param gameScreen
* Gamescreen to which AI belongs
*/
public AISpaceship(float startX, float startY, ShipBehaviour shipBehaviour,
SteeringDemoGameScreen gameScreen) {
super(startX, startY, 50.0f, 50.0f, null, gameScreen);
mShipBehaviour = shipBehaviour;
visible = true;
switch (mShipBehaviour) {
case Turret:
maxAcceleration = 0.0f;
maxVelocity = 0.0f;
mBitmap = gameScreen.getGame().getAssetManager().getBitmap("Turret");
break;
case Seeker:
maxAcceleration = -40.0f;
maxVelocity = 50.0f;
mBitmap = gameScreen.getGame().getAssetManager().getBitmap("enemy sprites 7");
break;
}
}
// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////
/*
* (non-Javadoc)
*
* #see
* uk.ac.qub.eeecs.gage.world.Sprite#update(uk.ac.qub.eeecs.gage.engine.
* ElapsedTime)
*/
#Override
public void update(ElapsedTime elapsedTime) {
switch (mShipBehaviour) {
case Seeker:
//Move down towards the player in a straight line
acceleration.y = RUN_ACCELERATION;
// Seek towards the player
// Try to avoid a collision with the playership
// Try to avoid a collision with the other spaceships
// Try to avoid a collision with the asteroids
SteeringBehaviours.separate(this,
((SteeringDemoGameScreen) mGameScreen).getAsteroids(),
separateThresholdAsteroid, 1.0f, accComponent);
accAccumulator.add(accComponent);
// If we are trying to avoid a collision then combine
// it with the seek behaviour, placing more emphasis on
// avoiding the collision.
if (!accAccumulator.isZero()) {
acceleration.x = 0.3f * acceleration.x + 0.7f
* accAccumulator.x;
acceleration.y = 0.3f * acceleration.y + 0.7f
* accAccumulator.y;
}
// Make sure we point in the direction of travel.
break;
}
// Call the sprite's superclass to apply the determine accelerations
super.update(elapsedTime);
// Check that our new position has not collided by one of the
// defined projectiles. If so, then removing any overlap and
// ensure a valid velocity.
checkForAndResolveCollisions(projectiles);
}
/**
* Check for and then resolve any collision between the AiShip and the
* player projectiles.
*
* #param projectiles
* Array of projectiles to test for collision against
*/
private void checkForAndResolveCollisions(GameObject[] projectiles) {
CollisionType collisionType;
// Consider each platform for a collision
for (GameObject projectile : projectiles) {
collisionType =
CollisionDetector.determineAndResolveCollision(this, Projectile);
switch (collisionType) {
case destroy:
visible = false;
break;
}
}
}
}
Projectile1 (The class used to determine all the game's projectiles and their behavoir).
package uk.ac.qub.eeecs.game.spaceDemo;
import uk.ac.qub.eeecs.gage.ai.SteeringBehaviours;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.util.Vector2;
import uk.ac.qub.eeecs.gage.world.Sprite;
/**
* AI controlled spaceship
*
* #version 1.0
*/
public class Projectile1 extends Sprite {
// /////////////////////////////////////////////////////////////////////////
// Properties
// /////////////////////////////////////////////////////////////////////////
/**
* AI control behaviour
*/
/**
* Acceleration with which the projectile can move along
* the x-axis
*/
private float RUN_ACCELERATION = 150.0f;
public enum ShipBehaviour {
bullet
}
private ShipBehaviour mShipBehaviour;
/**
* Distance at which the spaceship should avoid other game objects
*/
/**
* Accumulators used to build up the net steering outcome
*/
private Vector2 accAccumulator = new Vector2();
private Vector2 accComponent = new Vector2();
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a AI controlled spaceship
*
* #param startX
* x location of the AI spaceship
* #param startY
* y location of the AI spaceship
* #param shipBehaviour
* Steering behaviour to be used by the AI ship
* #param gameScreen
* Gamescreen to which AI belongs
*/
public Projectile1(float startX, float startY, ShipBehaviour shipBehaviour,
SteeringDemoGameScreen gameScreen) {
super(startX, startY, 50.0f, 50.0f, null, gameScreen);
mShipBehaviour = shipBehaviour;
switch (mShipBehaviour) {
case bullet:
maxAcceleration = 20.0f;
maxVelocity = 30.0f;
mBitmap = gameScreen.getGame().getAssetManager().getBitmap("Spaceship2");
break;
}
}
// /////////////////////////////////////////////////////////////////////////
// Methods
// /////////////////////////////////////////////////////////////////////////
/*
* (non-Javadoc)
*
* #see
* uk.ac.qub.eeecs.gage.world.Sprite#update(uk.ac.qub.eeecs.gage.engine.
* ElapsedTime)
*/
#Override
public void update(ElapsedTime elapsedTime) {
switch (mShipBehaviour) {
case bullet:
// Seek towards the player
acceleration.y = RUN_ACCELERATION;
break;
}
// Call the sprite's superclass to apply the determine accelerations
super.update(elapsedTime);
}
}
SterringGameDemo (The main class for the level on which the game play occurs and where the player, enemies and projectiles behave and interact with each other (note-it is called "SteeringGameDemo" because this is a leftover from a template we used to help make the class and has not been renamed yet)).
package uk.ac.qub.eeecs.game.spaceDemo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import uk.ac.qub.eeecs.gage.Game;
import uk.ac.qub.eeecs.gage.engine.AssetStore;
import uk.ac.qub.eeecs.gage.engine.ElapsedTime;
import uk.ac.qub.eeecs.gage.engine.graphics.IGraphics2D;
import uk.ac.qub.eeecs.gage.util.BoundingBox;
import uk.ac.qub.eeecs.gage.util.CollisionDetector;
import uk.ac.qub.eeecs.gage.world.GameObject;
import uk.ac.qub.eeecs.gage.world.GameScreen;
import uk.ac.qub.eeecs.gage.world.LayerViewport;
import uk.ac.qub.eeecs.gage.world.ScreenViewport;
import android.graphics.Color;
/**
* Simple steering game world
*
* #version 1.0
*/
public class SteeringDemoGameScreen extends GameScreen {
// /////////////////////////////////////////////////////////////////////////
// Properties
// /////////////////////////////////////////////////////////////////////////
/**
* Width and height of the level
*/
private final float LEVEL_WIDTH = 480.0f;
private final float LEVEL_HEIGHT = 280.0f;
/**
* Define viewports for this layer and the associated screen projection
*/
private ScreenViewport mScreenViewport;
private LayerViewport mLayerViewport;
/**
* Define a background object, alongside a player controlled
* space ship and separate lists of asteroids and AI controlled
* space ships.
*/
private GameObject mSpaceBackground;
private PlayerSpaceship mPlayerSpaceship;
private final int NUM_ASTEROIDS = 0;
private List<Asteroid> mAsteroids;
private final int NUM_SEEKERS = 2;
private final int NUM_TURRETS = 1;
private final int NUM_PROJECTILE = 1;
private List<Projectile1> mProjectile1;
private List<AISpaceship> mAISpaceships;
// /////////////////////////////////////////////////////////////////////////
// Constructors
// /////////////////////////////////////////////////////////////////////////
/**
* Create a simple steering game world
*
* #param game
* Game to which this screen belongs
*/
public SteeringDemoGameScreen(Game game) {
super("SteeringDemoGameScreen", game);
// Create the screen viewport
mScreenViewport = new ScreenViewport(0, 0, game.getScreenWidth(),
game.getScreenHeight());
// Create the layer viewport, taking into account the orientation
// and aspect ratio of the screen.
if (mScreenViewport.width > mScreenViewport.height)
mLayerViewport = new LayerViewport(240.0f, 240.0f
* mScreenViewport.height / mScreenViewport.width, 240,
240.0f * mScreenViewport.height / mScreenViewport.width);
else
mLayerViewport = new LayerViewport(240.0f * mScreenViewport.height
/ mScreenViewport.width, 240.0f, 240.0f
* mScreenViewport.height / mScreenViewport.width, 240);
// Load in the assets used by the steering demo
AssetStore assetManager = mGame.getAssetManager();
assetManager.loadAndAddBitmap("Space BG 2", "img/Space BG 2.png");
assetManager.loadAndAddBitmap("Asteroid1", "img/Asteroid1.png");
assetManager.loadAndAddBitmap("Asteroid2", "img/Asteroid2.png");
assetManager.loadAndAddBitmap("enemy sprites 7", "img/enemy sprites 7.png");
assetManager.loadAndAddBitmap("Spaceship2", "img/Spaceship2.png");
assetManager.loadAndAddBitmap("Spaceship3", "img/Spaceship3.png");
assetManager.loadAndAddBitmap("Spaceship3", "img/Spaceship3.png");
assetManager.loadAndAddBitmap("Player sprite", "img/Player sprite.png");
assetManager.loadAndAddBitmap("Turret", "img/Turret.png");
// Create the space background
mSpaceBackground = new GameObject(LEVEL_WIDTH / 2.0f,
LEVEL_HEIGHT / 2.0f, LEVEL_WIDTH, LEVEL_HEIGHT, getGame()
.getAssetManager().getBitmap("Space BG 2"), this);
// Create the player spaceship
mPlayerSpaceship = new PlayerSpaceship(230, 10, this);
// Create a number of randomly positioned asteroids
Random random = new Random();
mAsteroids = new ArrayList<Asteroid>(NUM_ASTEROIDS);
for (int idx = 0; idx < NUM_ASTEROIDS; idx++)
mAsteroids.add(new Asteroid(random.nextFloat() * LEVEL_WIDTH, random.nextFloat() * LEVEL_HEIGHT, this));
// Create a number of randomly positioned AI controlled ships
mAISpaceships = new ArrayList<AISpaceship>(NUM_SEEKERS + NUM_TURRETS);
for (int idx = 0; idx < NUM_SEEKERS; idx++)
mAISpaceships.add(new AISpaceship(random.nextFloat() * LEVEL_WIDTH,
180 + random.nextFloat() * LEVEL_HEIGHT,
AISpaceship.ShipBehaviour.Seeker, this));
for (int idx = 0; idx < NUM_TURRETS; idx++)
mAISpaceships.add(new AISpaceship(random.nextFloat() * LEVEL_WIDTH,
random.nextFloat() * LEVEL_HEIGHT,
AISpaceship.ShipBehaviour.Turret, this));
//Use the above to help spawn the bullets
mProjectile1 = new ArrayList<Projectile1>(NUM_PROJECTILE);
for (int idx = 0; idx < NUM_PROJECTILE; idx++)
mProjectile1.add(new Projectile1(mPlayerSpaceship.position.x, mPlayerSpaceship.position.y,
Projectile1.ShipBehaviour.bullet, this));
}
// /////////////////////////////////////////////////////////////////////////
// Support methods
// /////////////////////////////////////////////////////////////////////////
/**
* Return the player spaceship
*
* #return Player spaceship
*/
public PlayerSpaceship getPlayerSpaceship() {
return mPlayerSpaceship;
}
public List<Projectile1> getProjectile1() {
return mProjectile1;
}
/**
* Return a list of the AI spaceships in the level
*
* #return List of AI controlled spaceships
*/
public List<AISpaceship> getAISpaceships() {
return mAISpaceships;
}
/**
* Return a list of asteroids in the the level
*
* #return List of asteroids in the level
*/
public List<Asteroid> getAsteroids() {
return mAsteroids;
}
// /////////////////////////////////////////////////////////////////////////
// Update and Draw methods
// /////////////////////////////////////////////////////////////////////////
/*
* (non-Javadoc) fs
*
* #see
* uk.ac.qub.eeecs.gage.world.GameScreen#update(uk.ac.qub.eeecs.gage.engine
* .ElapsedTime)
*/
#Override
public void update(ElapsedTime elapsedTime) {
// Update the player spaceship
mPlayerSpaceship.update(elapsedTime);
// Ensure the player cannot leave the confines of the world
BoundingBox playerBound = mPlayerSpaceship.getBound();
if (playerBound.getLeft() < 0)
mPlayerSpaceship.position.x -= playerBound.getLeft();
else if (playerBound.getRight() > LEVEL_WIDTH)
mPlayerSpaceship.position.x -= (playerBound.getRight() - LEVEL_WIDTH);
if (playerBound.getBottom() < 0)
mPlayerSpaceship.position.y -= playerBound.getBottom();
else if (playerBound.getTop() > LEVEL_HEIGHT)
mPlayerSpaceship.position.y -= (playerBound.getTop() - LEVEL_HEIGHT);
// Ensure the enemyships cannot leave the confines of the world
//Use the above for player and enemies bullets in this class
//IMPORTANT - THE below code is VITAL for the collision detection and was a
//added by the tutor. It calls the bounding box and collision detector for the
//player and enemy ship. Add this to the above section where the bounding box
// is already called, just below the update method.
BoundingBox playersBound = mPlayerSpaceship.getBound();
for (AISpaceship aiSpaceship : mAISpaceships) {
BoundingBox aiBound = aiSpaceship.getBound();
if(CollisionDetector.isCollision(playersBound, aiBound)) {
}
}
// Focus the layer viewport on the player
// Ensure the viewport cannot leave the confines of the world
if (mLayerViewport.getLeft() < 0)
mLayerViewport.x -= mLayerViewport.getLeft();
else if (mLayerViewport.getRight() > LEVEL_WIDTH)
mLayerViewport.x -= (mLayerViewport.getRight() - LEVEL_WIDTH);
if (mLayerViewport.getBottom() < 0)
mLayerViewport.y -= mLayerViewport.getBottom();
else if (mLayerViewport.getTop() > LEVEL_HEIGHT)
mLayerViewport.y -= (mLayerViewport.getTop() - LEVEL_HEIGHT);
// Update each of the AI controlled spaceships
for (AISpaceship aiSpaceship : mAISpaceships)
aiSpaceship.update(elapsedTime);
// Update each of the asteroids
for (Asteroid asteroid : mAsteroids)
asteroid.update(elapsedTime);
// Update each of the Player Projectile1
for (Projectile1 projectile1 : mProjectile1)
projectile1.update(elapsedTime);
}
/*
* (non-Javadoc)
*
* #see
* uk.ac.qub.eeecs.gage.world.GameScreen#draw(uk.ac.qub.eeecs.gage.engine
* .ElapsedTime, uk.ac.qub.eeecs.gage.engine.graphics.IGraphics2D)
*/
#Override
public void draw(ElapsedTime elapsedTime, IGraphics2D graphics2D) {
// Create the screen to black and define a clip based on the viewport
graphics2D.clear(Color.BLACK);
graphics2D.clipRect(mScreenViewport.toRect());
// Draw the background first of all
mSpaceBackground.draw(elapsedTime, graphics2D, mLayerViewport,
mScreenViewport);
// Draw each of the asteroids
for (Asteroid asteroid : mAsteroids)
asteroid.draw(elapsedTime, graphics2D, mLayerViewport,
mScreenViewport);
// Draw each of the AI controlled spaceships
for (Projectile1 projectile1 : mProjectile1)
projectile1.draw(elapsedTime, graphics2D, mLayerViewport,
mScreenViewport);
// Draw each of the AI controlled spaceships
for (AISpaceship aiSpaceship : mAISpaceships)
aiSpaceship.draw(elapsedTime, graphics2D, mLayerViewport,
mScreenViewport);
// Draw the player
mPlayerSpaceship.draw(elapsedTime, graphics2D, mLayerViewport,
mScreenViewport);
}
}
Again, I say thank you to anyone who can help.
I've done something similar before.
Try to create a rectangle for every Bullet, Enemy or whatever you've got. The rectangles should have a size thats about the size of the graphics used. Of course the rectangles have to move with the graphics.
You can check if a collision occured simply with
if(rectangleA.intersects(rectangleB)){
doAwesomeStuff();
}
I hope that helps.