Android Bitmap : collision Detecting - java

I am writing an Android game right now and I would need some help in the collision of the wall on screen. When I drag the ball in the top and right it able to collide in wall but when I drag it faster it was able to overlap in the wall
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
// if the player moves
case MotionEvent.ACTION_MOVE: {
if (playerTouchRect.contains(x, y)) {
boolean left = false;
boolean right = false;
boolean up = false;
boolean down = false;
boolean canMove = false;
boolean foundFinish = false;
if (x != pLastXPos) {
if (x < pLastXPos) {
left = true;
} else {
right = true;
}
pLastXPos = x;
}
if (y != pLastYPos) {
if (y < pLastYPos) {
up = true;
} else {
down = true;
}
pLastYPos = y;
}
plCellRect = getRectFromPos(x, y);
newplRect.set(playerRect);
newplRect.left = x - (int) (playerRect.width() / 2);
newplRect.right = x + (int) (playerRect.width() / 2);
newplRect.top = y - (int) (playerRect.height() / 2);
newplRect.bottom = y + (int) (playerRect.height() / 2);
int currentRow = 0;
int currentCol = 0;
currentRow = getRowFromYPos(newplRect.top);
currentCol = getColFromXPos(newplRect.right);
if(!canMove){
canMove = mapManager.getCurrentTile().pMaze[currentRow][currentCol] == Cell.wall;
canMove =true;
}
finishTest = mapManager.getCurrentTile().pMaze[currentRow][currentCol];
foundA = finishTest == Cell.valueOf(letterNotGet + "");
canMove = mapManager.getCurrentTile().pMaze[currentRow][currentCol] != Cell.wall;
canMove = (finishTest == Cell.floor || finishTest == Cell.pl) && canMove;
if (canMove) {
invalidate();
setTitle();
}
if (foundA) {
mapManager.getCurrentTile().pMaze[currentRow][currentCol] = Cell.floor;
// finishTest
letterGotten.add(letterNotGet);
playCurrentLetter();
/*sounds.play(sExplosion, 1.0f, 1.0f, 0, 0, 1.5f);*/
foundS = letterNotGet == 's';
letterNotGet++;
}if(foundS){
AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);
builder.setTitle(mainActivity.getText(R.string.finished_title));
LayoutInflater inflater = mainActivity.getLayoutInflater();
View view = inflater.inflate(R.layout.finish, null);
builder.setView(view);
View closeButton =view.findViewById(R.id.closeGame);
closeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View clicked) {
if(clicked.getId() == R.id.closeGame) {
mainActivity.finish();
}
}
});
AlertDialog finishDialog = builder.create();
finishDialog.show();
}
else {
Log.d(TAG, "INFO: updated player position");
playerRect.set(newplRect);
setTouchZone();
updatePlayerCell();
}
} // end of (CASE) if playerTouch
break;
} // end of (SWITCH) Case motion
}//end of Switch
return true;
}//end of TouchEvent
private void finish() {
// TODO Auto-generated method stub
}
public int getColFromXPos(int xPos) {
val = xPos / (pvWidth / mapManager.getCurrentTile().pCols);
if (val == mapManager.getCurrentTile().pCols) {
val = mapManager.getCurrentTile().pCols - 1;
}
return val;
}
/**
* Given a y pixel position, return the row of the cell it is in This is
* used when determining the type of adjacent Cells.
*
* #param yPos
* y position in pixels
* #return The cell this position is in
*/
public int getRowFromYPos(int yPos) {
val = yPos / (pvHeight / mapManager.getCurrentTile().pRows);
if (val == mapManager.getCurrentTile().pRows) {
val = mapManager.getCurrentTile().pRows - 1;
}
return val;
}
/**
* When preserving the position we need to know which cell the player is in,
* so calculate it from the centre on its Rect
*/
public void updatePlayerCell() {
plCell.x = (playerRect.left + (playerRect.width() / 2))
/ (pvWidth / mapManager.getCurrentTile().pCols);
plCell.y = (playerRect.top + (playerRect.height() / 2))
/ (pvHeight / mapManager.getCurrentTile().pRows);
if (mapManager.getCurrentTile().pMaze[plCell.y][plCell.x] == Cell.floor) {
for (int row = 0; row < mapManager.getCurrentTile().pRows; row++) {
for (int col = 0; col < mapManager.getCurrentTile().pCols; col++) {
if (mapManager.getCurrentTile().pMaze[row][col] == Cell.pl) {
mapManager.getCurrentTile().pMaze[row][col] = Cell.floor;
break;
}
}
}
mapManager.getCurrentTile().pMaze[plCell.y][plCell.x] = Cell.pl;
}
}
public Rect getRectFromPos(int x, int y) {
calcCell.left = ((x / cellWidth) + 0) * cellWidth;
calcCell.right = calcCell.left + cellWidth;
calcCell.top = ((y / cellHeight) + 0) * cellHeight;
calcCell.bottom = calcCell.top + cellHeight;
Log.d(TAG, "Rect: " + calcCell + " Player: " + playerRect);
return calcCell;
}
public void setPlayerRect(Rect newplRect) {
playerRect.set(newplRect);
}
private void setTouchZone() {
playerTouchRect.set(
playerRect.left - playerRect.width() / TOUCH_ZONE,
playerRect.top - playerRect.height() / TOUCH_ZONE,
playerRect.right + playerRect.width() / TOUCH_ZONE,
playerRect.bottom + playerRect.height() / TOUCH_ZONE);
}
public Rect getPlayerRect() {
return playerRect;
}
public Point getPlayerCell() {
return plCell;
}
public void setPlayerCell(Point cell) {
plCell = cell;
}
}*

This is an architectural problem. You can read more about it here.
In essence, your physics simulation (as simple as might be) is coupled to your framerate (which is capped at 60fps). Any events occurring faster than 60Hz cannot be processed accurately hence your bug.
The solution is to run the collision detection on an independent thread and draw the state it calculates at 60fps.
Also, take a look at these gamedev questions that refer to the same article.

Related

How do I make resizing images while displaying my screen not be inefficient in processing.core (PApplet)

I am new to coding and don't really know what I am doing. I know that there my code is pretty inefficient and I could probably fix it later (i.e. collision and movement), but I have what I think is an efficient map setup. It only renders what is either around the camera or is around the player based on the camera type (freeRoam vs placerCentered). This works and runs fine until I start to resize pictures when I zoom in and out my map. I think that every time I draw a resized image my computer resizes it and then draws it for every instance of it rather than resizing it once and drawing it. Would I have to make images for every instance of zooming in or out or is there an optimized resizing tool that I am missing?
Google Drive link with images if anyone is interested: https://drive.google.com/drive/folders/1s5_9YPX7_6QWaxZPexN7pWk8zgmaT0w5?usp=sharing
(All images are 20 x 20 I think besides warrior1 and stoneTile1 which are both 32 x 32)
import processing.core.*;
import processing.event.MouseEvent;
import java.util.*;
public class Dungeon1 extends PApplet implements MyLibrary {
static gameState currentState;
static cameraState currentCameraState = cameraState.playerCentered;
enum gameState {
Over, Running
}
enum cameraState {
freeRoam, playerCentered
}
int cameraScaling = 20;
PImage testBoots;
PImage clown;
PImage testSquare;
PImage stoneTile1;
PImage resizedTile;
PImage warrior1;
float enemyX = 10;
float enemyY = 10;
float redSquare;
int currentXPos = 10;
int currentYPos = 10;
int tileType;
int mapWidth = 500;
int mapHeight = 500;
int[][] squareType = new int[mapHeight][mapWidth];
boolean movingUp, movingDown, movingLeft, movingRight;
int squareX;
int squareY;
boolean canMoveUp, canMoveDown, canMoveLeft, canMoveRight = true;
int cameraX = currentXPos;
int cameraY = currentYPos;
int lastCameraX;
int lastCameraY;
boolean centerCamera = false;
int pastMouseXPosition;
int pastMouseYPosition;
int squareWidth;
int squareHeight;
boolean mouseDragging = true, freeRoam, playerCentered;
public static void main(String[] args) {
PApplet.main("Dungeon1");
}
public void settings() {
fullScreen();
}
public void setup() {
playerStartPos();
createMap();
currentState = gameState.Running;
testBoots = loadImage("Images/TestBoots.png");
clown = loadImage("Images/clown.png");
testSquare = loadImage("Images/testSquare.png");
stoneTile1 = loadImage("Images/stoneTile1.png");
resizedTile = loadImage("Images/resizedTile.png");
warrior1 = loadImage("Images/warrior1.png");
}
public void draw() {
background(0, 0, 0);
drawMap();
isMoving();
}
public void keyPressed() {
if (key == 'w') {
movingUp = true;
}
if (key == 's') {
movingDown = true;
}
if (key == 'a') {
movingLeft = true;
}
if (key == 'd') {
movingRight = true;
}
if (key == 'k') {
Test = true;
}
if (key == 'c') {
currentCameraState = cameraState.playerCentered;
centerCamera = true;
}
if (key == 'v') {
currentCameraState = cameraState.freeRoam;
}
if (key == 'b') {
currentCameraState = cameraState.playerCentered;
}
}
public void keyReleased() {
if (key == 'w') {
movingUp = false;
}
if (key == 's') {
movingDown = false;
}
if (key == 'a') {
movingLeft = false;
}
if (key == 'd') {
movingRight = false;
}
if (key == 'k') {
Test = false;
}
}
public void mouseDragged(MouseEvent e) {
if (pastMouseXPosition < mouseX) {
lastCameraX--;
}
if (pastMouseXPosition > mouseX) {
lastCameraX++;
}
if (pastMouseYPosition < mouseY) {
lastCameraY--;
}
if (pastMouseYPosition > mouseY) {
lastCameraY++;
}
pastMouseXPosition = mouseX;
pastMouseYPosition = mouseY;
}
public void mousePressed() {
currentCameraState = cameraState.freeRoam;
mouseDragging = true;
pastMouseXPosition = mouseX;
pastMouseYPosition = mouseY;
}
public void mouseReleased() {
mouseDragging = false;
}
public void mouseWheel(MouseEvent e) {
if (e.getAmount() < 0) {
cameraScaling--;
} else {
cameraScaling++;
}
}
public void isMoving() {
if (movingUp) {
if (canMoveUp) {
currentYPos--;
cameraY--;
}
if (squareType[currentXPos][currentYPos] == 2) {
currentYPos++;
canMoveUp = false;
cameraY++;
}
}
if (movingDown) {
if (canMoveDown) {
currentYPos++;
cameraY++;
}
if (squareType[currentXPos][currentYPos] == 2) {
currentYPos--;
canMoveDown = false;
cameraY--;
}
}
if (movingRight) {
if (canMoveRight) {
currentXPos++;
cameraX++;
}
if (squareType[currentXPos][currentYPos] == 2) {
currentXPos--;
canMoveRight = false;
cameraX--;
}
}
if (movingLeft) {
if (canMoveLeft) {
currentXPos--;
cameraX--;
}
if (squareType[currentXPos][currentYPos] == 2) {
currentXPos++;
canMoveLeft = false;
cameraX++;
}
}
if (squareType[currentXPos][currentYPos] != 2) {
drawPlayer();
canMoveUp = true;
canMoveLeft = true;
canMoveDown = true;
canMoveRight = true;
}
}
public void playerStartPos() {
currentXPos = 96/2;
currentYPos = 54/2;
}
public void drawPlayer() {
if (currentCameraState == cameraState.playerCentered) {
image(testSquare, (currentXPos * cameraScaling - cameraX * cameraScaling), (currentYPos * cameraScaling - cameraY * cameraScaling), cameraScaling, cameraScaling);
} else if (currentCameraState == cameraState.freeRoam) {
image(testSquare, (currentXPos * cameraScaling - lastCameraX * cameraScaling), (currentYPos * cameraScaling - lastCameraY * cameraScaling), cameraScaling, cameraScaling);
}
}
public void createMap() {
//First creates the plane to navigate
for (int r = 0; r < mapHeight; r++) {
for (int c = 0; c < mapWidth; c++) {
redSquare = generator.nextInt(100);
if (redSquare > 90) {
tileType = 2;
} else {
tileType = 0;
}
squareType[r][c] = tileType;
}
}
}
public void drawMap() {
//TODO: Either figure out how to make actual scaled images not run poorly or make a bunch of images that only are displayed a scaled resolution
if (cameraScaling < 15) {
cameraScaling = 15;
}
if (cameraScaling > 50) {
cameraScaling = 50;
}
if (centerCamera) {
cameraX = currentXPos - 47*20/ cameraScaling;
cameraY = currentYPos - 23*20/ cameraScaling;
}
squareWidth = 20+ cameraScaling;
squareHeight = 20+ cameraScaling;
centerCamera = false;
if (currentCameraState == cameraState.freeRoam) {
cameraX = lastCameraX;
cameraY = lastCameraY;
for (int r = lastCameraX; r < lastCameraX + 96*20/ cameraScaling + 2 ; r++) {
for (int c = lastCameraY; c < lastCameraY + 54*20/ cameraScaling + 2; c++) {
squareX = r * cameraScaling - lastCameraX * cameraScaling;
squareY = c * cameraScaling - lastCameraY * cameraScaling;
if (r > 0 && c > 0 && r < mapWidth && c < mapHeight) {
if (squareType[r][c] == 0) {
image(resizedTile, squareX, squareY, cameraScaling, cameraScaling);
} else {
colorTiles(r, c);
rect(squareX, squareY, cameraScaling, cameraScaling);
}
}
}
}
} else if (currentCameraState == cameraState.playerCentered) {
lastCameraX = cameraX;
lastCameraY = cameraY;
for (int r = cameraX; r < cameraX + 96*20/ cameraScaling + 2; r++) {
for (int c = cameraY; c < cameraY + 54*20/ cameraScaling + 2; c++) {
squareX = r * cameraScaling - cameraX * cameraScaling;
squareY = c * cameraScaling - cameraY * cameraScaling;
if (r > 0 && c > 0 && r < mapWidth && c < mapHeight) {
if (squareType[r][c] == 0) {
image(resizedTile, squareX, squareY, cameraScaling, cameraScaling);
} else {
colorTiles(r, c);
rect(squareX, squareY, cameraScaling, cameraScaling);
}
}
}
}
}
}
public void colorTiles(int r, int c) {
if (squareType[r][c] == 2) {
fill(255, 0, 0);
}
}
}```

Why is my Java range slider not able to slide both thumbs at once?

I am currently trying to implement a feature in this range slider where the user can click the space in between the upper thumb and lower thumb then drag to move both thumbs at once to update the range values. These are the classes I am working with:
RangeSlider.java: https://pastebin.com/APxuaEwP
RangeSliderUI.java: https://pastebin.com/aUy7cLni
RangeSliderDemo.java: https://pastebin.com/BNLzxNtJ
I have implemented components in the RangeTrackListener in RangeSliderUI.java that serve to move both thumbs when the user drags the space between. Here is the RangeTrackListener with changes made:
/**
* Listener to handle mouse movements in the slider track.
*/
public class RangeTrackListener extends TrackListener {
private boolean windowSliding;
private double previousY;
private void updateRectanglesForSlidingWindow(MouseEvent e) {
if(windowSliding) {
double diff = previousY - e.getY();
int upperThumbRectNewY = (int) Math.floor(upperThumbRect.getY() - diff);
int thumbRectNewY = (int) Math.floor(thumbRect.getY() - diff);
if(upperThumbRectNewY < yPositionForValue(slider.getMaximum()) && thumbRectNewY > yPositionForValue(slider.getMinimum())) {
upperThumbRect.setLocation((int) Math.floor(upperThumbRect.getX()), upperThumbRectNewY);
thumbRect.setLocation((int) Math.floor(thumbRect.getX()), thumbRectNewY);
previousY = e.getY();
slider.repaint();
}
}
}
private void updateValuesForSlidingWindow(MouseEvent e) {
if(windowSliding) {
/*double diff = previousY - e.getY();
int upperThumbRectNewY = (int)Math.floor(upperThumbRect.getY() - diff);
int thumbRectNewY = (int)Math.floor(thumbRect.getY() - diff);
upperThumbRect.setLocation((int)Math.floor(upperThumbRect.getX()), upperThumbRectNewY);
thumbRect.setLocation((int)Math.floor(thumbRect.getX()), thumbRectNewY);*/
double prevVal = slider.getValue();
double newVal = valueForYPosition((int)Math.floor(thumbRect.getY()));
double valDiff = prevVal - newVal;
slider.setValue((int)Math.floor(newVal));
slider.setExtent((int)Math.floor(slider.getExtent() - valDiff));
windowSliding = false;
}
}
#Override
public void mousePressed(MouseEvent e) {
if (!slider.isEnabled()) {
return;
}
currentMouseX = e.getX();
currentMouseY = e.getY();
if(currentMouseY > thumbRect.getY() && !thumbRect.contains(0, currentMouseY) && currentMouseY < upperThumbRect.getY() && !upperThumbRect.contains(0, currentMouseY)) {
windowSliding = true;
previousY = currentMouseY;
}
if (slider.isRequestFocusEnabled()) {
slider.requestFocus();
}
// Determine which thumb is pressed. If the upper thumb is
// selected (last one dragged), then check its position first;
// otherwise check the position of the lower thumb first.
boolean lowerPressed = false;
boolean upperPressed = false;
if (upperThumbSelected || slider.getMinimum() == slider.getValue()) {
if (upperThumbRect.contains(currentMouseX, currentMouseY)) {
upperPressed = true;
} else if (thumbRect.contains(currentMouseX, currentMouseY)) {
lowerPressed = true;
}
} else {
if (thumbRect.contains(currentMouseX, currentMouseY)) {
lowerPressed = true;
} else if (upperThumbRect.contains(currentMouseX, currentMouseY)) {
upperPressed = true;
}
}
// Handle lower thumb pressed.
if (lowerPressed) {
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
offset = currentMouseY - thumbRect.y;
break;
case SwingConstants.HORIZONTAL:
offset = currentMouseX - thumbRect.x;
break;
}
upperThumbSelected = false;
lowerDragging = true;
return;
}
lowerDragging = false;
// Handle upper thumb pressed.
if (upperPressed) {
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
offset = currentMouseY - upperThumbRect.y;
break;
case SwingConstants.HORIZONTAL:
offset = currentMouseX - upperThumbRect.x;
break;
}
upperThumbSelected = true;
upperDragging = true;
return;
}
upperDragging = false;
}
#Override
public void mouseReleased(MouseEvent e) {
updateValuesForSlidingWindow(e);
lowerDragging = false;
upperDragging = false;
slider.setValueIsAdjusting(false);
super.mouseReleased(e);
}
#Override
public void mouseDragged(MouseEvent e) {
updateRectanglesForSlidingWindow(e);
if (!slider.isEnabled()) {
return;
}
currentMouseX = e.getX();
currentMouseY = e.getY();
if (lowerDragging) {
slider.setValueIsAdjusting(true);
moveLowerThumb();
} else if (upperDragging) {
slider.setValueIsAdjusting(true);
moveUpperThumb();
}
}
#Override
public boolean shouldScroll(int direction) {
return false;
}
/**
* Moves the location of the lower thumb, and sets its corresponding
* value in the slider.
*/
protected void moveLowerThumb() {
int thumbMiddle = 0;
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
int halfThumbHeight = thumbRect.height / 2;
int thumbTop = currentMouseY - offset;
int trackTop = trackRect.y;
int trackBottom = trackRect.y + (trackRect.height - 1);
int vMax = yPositionForValue(slider.getValue() + slider.getExtent());
// Apply bounds to thumb position.
if (drawInverted()) {
trackBottom = vMax;
} else {
trackTop = vMax;
}
thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
setThumbLocation(thumbRect.x, thumbTop);
// Update slider value.
thumbMiddle = thumbTop + halfThumbHeight;
slider.setValue(valueForYPosition(thumbMiddle));
break;
case SwingConstants.HORIZONTAL:
int halfThumbWidth = thumbRect.width / 2;
int thumbLeft = currentMouseX - offset;
int trackLeft = trackRect.x;
int trackRight = trackRect.x + (trackRect.width - 1);
int hMax = xPositionForValue(slider.getValue() + slider.getExtent());
// Apply bounds to thumb position.
if (drawInverted()) {
trackLeft = hMax;
} else {
trackRight = hMax;
}
thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
setThumbLocation(thumbLeft, thumbRect.y);
// Update slider value.
thumbMiddle = thumbLeft + halfThumbWidth;
slider.setValue(valueForXPosition(thumbMiddle));
break;
default:
return;
}
}
/**
* Moves the location of the upper thumb, and sets its corresponding
* value in the slider.
*/
protected void moveUpperThumb() {
int thumbMiddle = 0;
switch (slider.getOrientation()) {
case SwingConstants.VERTICAL:
int halfThumbHeight = thumbRect.height / 2;
int thumbTop = currentMouseY - offset;
int trackTop = trackRect.y;
int trackBottom = trackRect.y + (trackRect.height - 1);
int vMin = yPositionForValue(slider.getValue());
// Apply bounds to thumb position.
if (drawInverted()) {
trackTop = vMin;
} else {
trackBottom = vMin;
}
thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
setUpperThumbLocation(thumbRect.x, thumbTop);
// Update slider extent.
thumbMiddle = thumbTop + halfThumbHeight;
slider.setExtent(valueForYPosition(thumbMiddle) - slider.getValue());
break;
case SwingConstants.HORIZONTAL:
int halfThumbWidth = thumbRect.width / 2;
int thumbLeft = currentMouseX - offset;
int trackLeft = trackRect.x;
int trackRight = trackRect.x + (trackRect.width - 1);
int hMin = xPositionForValue(slider.getValue());
// Apply bounds to thumb position.
if (drawInverted()) {
trackRight = hMin;
} else {
trackLeft = hMin;
}
thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
setUpperThumbLocation(thumbLeft, thumbRect.y);
// Update slider extent.
thumbMiddle = thumbLeft + halfThumbWidth;
slider.setExtent(valueForXPosition(thumbMiddle) - slider.getValue());
break;
default:
return;
}
}
}
It produces strange behavior like the following:
I am wondering if I am misunderstanding the use of the valueForYposition function. I have been banging my head on this for a little while now. I feel as if I am missing something obvious. I wish I could ask a more specific question regarding this code. I would really appreciate it if someone could take a look and enlighten me on any obvious mistakes. Thank you very much.
EDIT: The issue has been resolved. I was being naive with regards to how the setValue function worked in this implementation of RangeSlider. Thank you to all who took a look.

How to create easy, medium and hard level for tic-tac-toe-game?

Currently i'm creating tic-tac-toe-game in android
i'm successfully created game but facing some issues
Here is my code that i have tried so far
Here is My BoardView
public class BoardView extends View implements GestureDetector.OnGestureListener, ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
private static final int STROKE_WIDTH = 10;
private static final int SWEEPER_WIDTH = 20;
private float[] gridLinePoints;
private Paint gridPaint;
private PointF[][] centerPoints;
private Paint signPaint;
private List<SignData> signDataList;
private #Constants.WinLinePosition int winLinePosition;
private Paint winLinePaint;
private GestureDetector clickDetector;
private OnBoardInteractionListener onBoardInteractionListener;
private ValueAnimator clickAnimator;
private ValueAnimator winLineAnimator;
private ValueAnimator resetAnimator;
private float signRadius;
private float winLineLength;
private float sweeperStartPosition;
private Paint sweeperPaint;
private int[] sweeperColors;
private float[] sweeperStops;
public BoardView(Context context) {
super(context);
init();
}
public BoardView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BoardView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.M)
private void init() {
gridLinePoints = new float[16];
centerPoints = new PointF[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j] = new PointF();
}
}
signDataList = new ArrayList<>();
winLinePosition = Constants.NONE;
gridPaint = new Paint();
gridPaint.setColor(getContext().getResources().getColor(R.color.holo_green_dark, null));
gridPaint.setAntiAlias(true);
gridPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
gridPaint.setStrokeCap(Paint.Cap.ROUND);
signPaint = new Paint();
signPaint.setColor(getContext().getResources().getColor(R.color.holo_orange_dark, null));
signPaint.setAntiAlias(true);
signPaint.setStyle(Paint.Style.STROKE);
signPaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
signPaint.setStrokeCap(Paint.Cap.ROUND);
winLinePaint = new Paint();
winLinePaint.setColor(getContext().getResources().getColor(R.color.holo_red_dark, null));
winLinePaint.setAntiAlias(true);
winLinePaint.setStrokeWidth(dpToPx(STROKE_WIDTH));
winLinePaint.setStrokeCap(Paint.Cap.ROUND);
clickDetector = new GestureDetector(getContext(), this);
clickAnimator = new ValueAnimator();
clickAnimator.setDuration(150);
clickAnimator.setInterpolator(new DecelerateInterpolator());
clickAnimator.addUpdateListener(this);
clickAnimator.addListener(this);
winLineAnimator = new ValueAnimator();
winLineAnimator.setDuration(150);
winLineAnimator.setInterpolator(new DecelerateInterpolator());
winLineAnimator.addUpdateListener(this);
winLineAnimator.addListener(this);
resetAnimator = new ValueAnimator();
resetAnimator.setDuration(500);
resetAnimator.setInterpolator(new AccelerateInterpolator());
resetAnimator.addUpdateListener(this);
resetAnimator.addListener(this);
sweeperPaint = new Paint();
sweeperPaint.setAntiAlias(true);
sweeperPaint.setStyle(Paint.Style.FILL);
sweeperColors = new int[3];
sweeperColors[0] = Color.parseColor("#0000DDFF");
sweeperColors[1] = Color.parseColor("#FF00DDFF");
sweeperColors[2] = Color.parseColor("#0000DDFF");
sweeperStops = new float[3];
sweeperStops[0] = 0;
sweeperStops[1] = 0.5f;
sweeperStops[2] = 1;
setLayerType(LAYER_TYPE_SOFTWARE, sweeperPaint);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
getLayoutParams().height = getMeasuredWidth();
setGridLinePoints();
setCenterPoints();
setAnimationValues();
}
#Override
protected void onDraw(Canvas canvas) {
drawGrid(canvas);
super.onDraw(canvas);
if (resetAnimator.isRunning()) {
canvas.clipRect(0, sweeperStartPosition, getMeasuredWidth(), getMeasuredWidth());
setSweeperGradient();
canvas.drawRect(0, sweeperStartPosition, getMeasuredWidth(), sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperPaint);
}
drawSigns(canvas);
drawWinLine(canvas);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if ((!isEnabled()) || (clickAnimator.isRunning()) || (isAnimationFlagSet())) {
return super.onTouchEvent(event);
} else {
return clickDetector.onTouchEvent(event);
}
}
private boolean isAnimationFlagSet() {
for (SignData signData : signDataList) {
if (signData.isAnimationFlag()) {
return true;
}
}
return false;
}
private void setGridLinePoints() {
int side = getMeasuredWidth();
float padding = dpToPx(STROKE_WIDTH / 2f);
gridLinePoints[0] = gridLinePoints[4] = gridLinePoints[9] = gridLinePoints[13] = padding;
gridLinePoints[1] = gridLinePoints[3] = gridLinePoints[8] = gridLinePoints[10] = side / 3f;
gridLinePoints[2] = gridLinePoints[6] = gridLinePoints[11] = gridLinePoints[15] = side - padding;
gridLinePoints[5] = gridLinePoints[7] = gridLinePoints[12] = gridLinePoints[14] = (2 * side) / 3f;
}
private void setCenterPoints() {
float a = getMeasuredWidth() / 6f;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
centerPoints[i][j].x = a + (j * (2 * a));
centerPoints[i][j].y = a + (i * (2 * a));
}
}
}
private void setAnimationValues() {
clickAnimator.setFloatValues(0, (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH));
winLineAnimator.setFloatValues(0, getMeasuredWidth());
resetAnimator.setFloatValues(-dpToPx(SWEEPER_WIDTH), getMeasuredWidth());
}
private void setSweeperGradient() {
float axis = sweeperStartPosition + (dpToPx(SWEEPER_WIDTH / 2f));
LinearGradient horizontalGradient = new LinearGradient(0, axis, getMeasuredWidth(), axis,
sweeperColors, sweeperStops, Shader.TileMode.CLAMP);
LinearGradient verticalGradient = new LinearGradient(getMeasuredWidth() / 2f, sweeperStartPosition,
getMeasuredWidth() / 2f, sweeperStartPosition + dpToPx(SWEEPER_WIDTH), sweeperColors, sweeperStops,
Shader.TileMode.CLAMP);
ComposeShader shader = new ComposeShader(horizontalGradient, verticalGradient, PorterDuff.Mode.MULTIPLY);
sweeperPaint.setShader(shader);
}
private void drawGrid(Canvas canvas) {
canvas.drawLines(gridLinePoints, gridPaint);
}
private void drawSigns(Canvas canvas) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
switch (signData.getSign()) {
case Constants.CIRCLE:
drawCircle(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.CROSS:
drawCross(canvas, centerPoints[signData.getRow()][signData.getColumn()], signData.isAnimationFlag());
break;
case Constants.EMPTY:
break;
}
}
}
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawCircle(center.x, center.y, radius, signPaint);
}
private void drawCross(Canvas canvas, PointF center, boolean animationFlag) {
float radius = animationFlag ? signRadius : (getMeasuredWidth() / 6f) - dpToPx(2 * STROKE_WIDTH);
canvas.drawLine(center.x - radius, center.y - radius, center.x + radius, center.y + radius, signPaint);
canvas.drawLine(center.x - radius, center.y + radius, center.x + radius, center.y - radius, signPaint);
}
private void drawWinLine(Canvas canvas) {
float length = winLineLength;
float a = getMeasuredWidth() / 6f;
float padding = dpToPx(STROKE_WIDTH);
switch (winLinePosition) {
case Constants.NONE:
break;
case Constants.ROW_1:
canvas.drawLine(padding, a, length - padding, a, winLinePaint);
break;
case Constants.ROW_2:
canvas.drawLine(padding, a + (2 * a), length - padding, a + (2 * a), winLinePaint);
break;
case Constants.ROW_3:
canvas.drawLine(padding, a + (4 * a), length - padding, a + (4 * a), winLinePaint);
break;
case Constants.COLUMN_1:
canvas.drawLine(a, padding, a, length - padding, winLinePaint);
break;
case Constants.COLUMN_2:
canvas.drawLine(a + (2 * a), padding, a + (2 * a), length - padding, winLinePaint);
break;
case Constants.COLUMN_3:
canvas.drawLine(a + (4 * a), padding, a + (4 * a), length - padding, winLinePaint);
break;
case Constants.DIAGONAL_1:
canvas.drawLine(padding, padding, length - padding, length - padding, winLinePaint);
break;
case Constants.DIAGONAL_2:
canvas.drawLine(getMeasuredWidth() - padding, padding, padding + getMeasuredWidth()
- length, length - padding, winLinePaint);
break;
}
}
void addSignToBoard(#Constants.Sign int sign, int row, int column) {
SignData signData = new SignData();
signData.setSign(sign);
signData.setRow(row);
signData.setColumn(column);
signData.setAnimationFlag(true);
if (clickAnimator.isRunning()) {
clickAnimator.end();
}
signDataList.add(signData);
clickAnimator.start();
}
void showWinLine(#Constants.WinLinePosition int winLinePosition) {
this.winLinePosition = winLinePosition;
winLineAnimator.start();
}
void resetBoard() {
if (!resetAnimator.isRunning()) {
resetAnimator.start();
}
}
boolean isAlreadyAdded(int row, int column) {
for (int i = 0; i < signDataList.size(); i++) {
SignData signData = signDataList.get(i);
if ((signData.getRow() == row) && (signData.getColumn() == column)) {
return true;
}
}
return false;
}
private float dpToPx(float dp) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
}
#Override
public boolean onDown(MotionEvent e) {
return true;
}
#Override
public void onShowPress(MotionEvent e) {
}
#Override
public boolean onSingleTapUp(MotionEvent e) {
float x = e.getX();
float y = e.getY();
int row = detectIndexOfPartition(y);
int column = detectIndexOfPartition(x);
if ((row != -1) && (column != -1)) {
onBoardInteractionListener.onBoardClick(BoardView.this, row, column);
}
return true;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
#Override
public void onLongPress(MotionEvent e) {
}
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
private int detectIndexOfPartition(float value) {
float maxValue = getMeasuredWidth();
float totalNumberOfPartitions = 3;
float lengthOfSinglePartition = maxValue / totalNumberOfPartitions;
return (int) (value / lengthOfSinglePartition);
}
public void setOnBoardInteractionListener(OnBoardInteractionListener onBoardInteractionListener) {
this.onBoardInteractionListener = onBoardInteractionListener;
}
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if (animation == clickAnimator) {
signRadius = (float) animation.getAnimatedValue();
} else if (animation == winLineAnimator) {
winLineLength = (float) animation.getAnimatedValue();
} else if (animation == resetAnimator) {
sweeperStartPosition = (float) animation.getAnimatedValue();
}
invalidate();
}
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
if (animation == clickAnimator) {
SignData signData = signDataList.get(signDataList.size() - 1);
signData.setAnimationFlag(false);
onBoardInteractionListener.onSignAdded(signData.getSign(), signData.getRow(), signData.getColumn());
signRadius = 0;
} else if (animation == resetAnimator) {
signDataList.clear();
winLinePosition = Constants.NONE;
onBoardInteractionListener.onBoardReset();
}
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
interface OnBoardInteractionListener {
void onBoardClick(BoardView board, int row, int column);
void onSignAdded(#Constants.Sign int sign, int row, int column);
void onBoardReset();
}
private class SignData {
private #Constants.Sign int sign;
private int row;
private int column;
private boolean animationFlag;
#Constants.Sign int getSign() {
return sign;
}
void setSign(#Constants.Sign int sign) {
this.sign = sign;
}
int getRow() {
return row;
}
void setRow(int row) {
this.row = row;
}
int getColumn() {
return column;
}
void setColumn(int column) {
this.column = column;
}
boolean isAnimationFlag() {
return animationFlag;
}
void setAnimationFlag(boolean animationFlag) {
this.animationFlag = animationFlag;
}
}
}
My Brain class
class Brain {
private static Brain INSTANCE;
private #Constants.Sign
int[][] board = new int[3][3];
private int rowOfResult;
private int columnOfResult;
private int depth;
private #Constants.Sign
int computerSign;
private #Constants.Sign
int playerSign;
private OnProcessCompleteListener onProcessCompleteListener;
private static final int HORIZONTAL = 0;
private static final int VERTICAL = 1;
private static final int DIAGONAL = 2;
#IntDef({HORIZONTAL, VERTICAL, DIAGONAL})
#interface DirectionOfWinLine {
}
// References used by isWin function.
private int[] winSequence = new int[3];
private int[] row = new int[3];
private int[] column = new int[3];
private int[] diagonal1 = new int[3];
private int[] diagonal2 = new int[3];
private Brain() {
}
static Brain getInstance() {
if (INSTANCE == null) {
INSTANCE = new Brain();
}
return INSTANCE;
}
void play() {
if (onProcessCompleteListener == null) {
return;
}
calculateNextMove(computerSign, depth);
onProcessCompleteListener.onNextMoveCalculated(rowOfResult, columnOfResult);
}
private int calculateNextMove(#Constants.Sign int sign, int depth) {
if (isWin(computerSign, false)) {
return 10 - depth;
} else if (isWin(playerSign, false)) {
return depth - 10;
}
if (depth >= 9) {
return 0;
}
List<Integer> scores = new ArrayList<>(), rowIndices = new ArrayList<>(), columnIndices = new ArrayList<>();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == Constants.EMPTY) {
board[i][j] = sign;
scores.add(calculateNextMove(getOppositeSign(sign), depth + 1));
rowIndices.add(i);
columnIndices.add(j);
board[i][j] = Constants.EMPTY;
}
}
}
if (sign == computerSign) {
int maxScore = -100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) > maxScore) {
maxScore = scores.get(i);
}
}
return randomizeScore(maxScore, scores, rowIndices, columnIndices);
} else {
int minScore = 100;
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) < minScore) {
minScore = scores.get(i);
}
}
return randomizeScore(minScore, scores, rowIndices, columnIndices);
}
}
private int randomizeScore(int score, List<Integer> scores, List<Integer> rowIndices, List<Integer> columnIndices) {
List<Integer> equalScoreIndices = new ArrayList<>();
for (int i = 0; i < scores.size(); i++) {
if (scores.get(i) == score) {
equalScoreIndices.add(i);
}
}
Random rand = new Random();
int randomIndex = equalScoreIndices.get(rand.nextInt(equalScoreIndices.size()));
rowOfResult = rowIndices.get(randomIndex);
columnOfResult = columnIndices.get(randomIndex);
return score;
}
private boolean isWin(#Constants.Sign int sign, boolean notifyWinEnabled) {
for (int i = 0; i < 3; i++) {
winSequence[i] = sign;
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
diagonal1[i] = board[i][j];
}
if ((i + j) == 2) {
diagonal2[i] = board[i][j];
}
row[j] = board[i][j];
column[j] = board[j][i];
}
if (isEqual(row, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, HORIZONTAL, i + 1);
}
return true;
} else if (isEqual(column, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, VERTICAL, i + 1);
}
return true;
}
}
if (isEqual(diagonal1, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 1);
}
return true;
} else if (isEqual(diagonal2, winSequence)) {
if (notifyWinEnabled) {
notifyWin(sign, DIAGONAL, 2);
}
return true;
}
return false;
}
private boolean isEqual(int[] x, int[] y) {
for (int i = 0; i < 3; i++) {
if (x[i] != y[i]) {
return false;
}
}
return true;
}
void analyzeBoard() {
if (onProcessCompleteListener == null) {
return;
}
if ((!isWin(Constants.CIRCLE, true)) && (!isWin(Constants.CROSS, true)) && (depth >= 9)) {
onProcessCompleteListener.onGameDraw();
}
}
private void notifyWin(#Constants.Sign int sign, #DirectionOfWinLine int direction, int index) {
if (onProcessCompleteListener == null) {
return;
}
#Constants.WinLinePosition int winLinePosition = Constants.NONE;
switch (direction) {
case HORIZONTAL:
switch (index) {
case 1:
winLinePosition = Constants.ROW_1;
break;
case 2:
winLinePosition = Constants.ROW_2;
break;
case 3:
winLinePosition = Constants.ROW_3;
break;
}
break;
case VERTICAL:
switch (index) {
case 1:
winLinePosition = Constants.COLUMN_1;
break;
case 2:
winLinePosition = Constants.COLUMN_2;
break;
case 3:
winLinePosition = Constants.COLUMN_3;
break;
}
break;
case DIAGONAL:
switch (index) {
case 1:
winLinePosition = Constants.DIAGONAL_1;
break;
case 2:
winLinePosition = Constants.DIAGONAL_2;
break;
}
break;
}
onProcessCompleteListener.onGameWin(sign, winLinePosition);
}
void reset() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = Constants.EMPTY;
}
}
depth = 0;
}
void setComputerSign(int computerSign) {
this.computerSign = computerSign;
playerSign = getOppositeSign(computerSign);
}
void updateBoard(#Constants.Sign int sign, int row, int column) {
board[row][column] = sign;
depth++;
}
private #Constants.Sign
int getOppositeSign(#Constants.Sign int sign) {
return sign == Constants.CIRCLE ? Constants.CROSS : Constants.CIRCLE;
}
void setOnProcessCompleteListener(OnProcessCompleteListener onProcessCompleteListener) {
this.onProcessCompleteListener = onProcessCompleteListener;
}
interface OnProcessCompleteListener {
void onNextMoveCalculated(int row, int column);
void onGameWin(#Constants.Sign int sign, #Constants.WinLinePosition int winLinePosition);
void onGameDraw();
}
void destroy() {
INSTANCE = null;
}
}
I have created github repo for this all code available here
https://github.com/SuperSaiyanGoku3/MyGame
I'm facing some issue in above code
The Above code only support hard level (Impossible), how can i create easy medium and hard level in above game algorithm again computer(CPU).
how can i set custom icon instead of O and X
how can i set all three game mode randomly in above code, as easy medium and hard level, so when user start the game, cpu will come randomly, wither easy or medium or hard.
Here are some links that i have tried so far but unable to understand how to create easy medium and hard level
Learn to create a Tic-Tac-Toe Game for Android
Android Studio - JAVA - TIC TAC TOE
Android tic tac toe game - logic issue
Tic-Tac-Toe (TableLayout) Android App
Tic Tac Toe Game
If need more information please do let me know. Thanks in advance. Your efforts will be appreciated.
Implement difficulty: For support easy and medium difficulty, I suggest you just use random. You have implemented HARD difficulty, so you just need lesser difficult logic that making "mistakes", and this mistake can be implemented by random.
private void calculateNextMoveRandom() {
Random rand = new Random();
int randomRow;
int randomColumn;
while (true) {
randomRow = rand.nextInt(3);
randomColumn = rand.nextInt(3);
if (Constants.EMPTY == board[randomRow][randomColumn]) {
rowOfResult = randomRow;
columnOfResult = randomColumn;
return;
}
}
}
2. **Bitmap marker:** You can use `BitmapFactory.decodeResource()` to draw bitmap on screen.
private void drawCircle(Canvas canvas, PointF center, boolean animationFlag) {
int iconSize = (int) LayoutUtil.getPixelFromDp(MARKER_SIZE, getContext());
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.android);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
canvas.drawBitmap(scaled, center.x - (iconSize >> 1), center.y - (iconSize >> 1), signPaint);
}
3. **Random difficulty:** Just use random to set difficulty.
brain.setDifficulty(new Random().nextInt(3));
Here is my pull request: (Fallen link)
https://github.com/SuperSaiyanGoku3/MyGame/pull/1
If you are using a minimax strategy to play tic-tac-toe, you can have multiple switch cases that correspond to different difficulty levels in the game. The most naive method of doing this is by putting different depth thresholds in the minimax tree. For example, you can expand the minimax game tree until only depth 2 (assuming for a 3*3 tic-tac-toe) for easy level and say until the end of the search tree for the highest difficulty level. You can set this threshold depending on the board size and the difficulty level expected.
Another way of implementing difficulty levels is by implementing different heuristic functions to calculate the board score (a goodness measure to the current state of the board). You can have your heuristic function that evaluates the individual row/column/diagonal score based on the number of cells that you occupy in that row/column/diagonal. For example, if the number of cells occupied is 1, then score = x (x can be any arbitrary number). If the number of cells occupied is 2, then score = x^2 and if it is 3, then score = x^3. Then multiply all individual line scores to get a measure of goodness (or you can think of this as a winning probability) in each row, column or diagonal. You can calculate a similar score for your opponent and then take a difference to get a board score. Thus the idea is to implement different ways of evaluating current board states by designing different variations of the heuristic function for each difficulty level. A bad heuristic function would work as an easy level since it will lose a lot of games whereas a properly designed heuristic will have a no-lose policy (it will end up winning or drawing the game).
In order to randomly choose a difficulty level at each run of the game, you can use a random number generator to pick a difficulty level. You can assign '0' as easy, '1' as medium and '2' as hard. You can use the following java function to generate an integer between 0 and (n-1)
int random = Random.nextInt(n)
Complete Tic Tac Toe with "n x n matrix"
Basic ideas is that we just have to check if column / row / diagonal elements are same.
code written in C#, with basic unit test.

Java: Super class won't override function

I have been working on a game for a while and I would like to have a different class for each type of Creature that there is. Right now, all of the different creatures' AI is run in a long switch and I would like a superclass to ovveride that function with that AI for that creature. I have this set up but it won't override.
Am I forgetting something?
Bunny.java:
package creature;
import org.newdawn.slick.opengl.Texture;
import creature.Creature;
import creature.CreatureType;
import data.Tile;
public class Bunny extends Creature{
public Bunny(CreatureType type, float x, float y, float speed1) {
super(type, x, y, speed1);
}
public void AI(int type) {
System.out.println("test");
}
}
Creature.java:
public Creature(CreatureType type, float x, float y, float speed1) {
this.texture = drawImg(type.textureName);
this.textureHamster = drawImg("creatures/HamsterFace");
this.healthBackground = drawImg("health_background");
this.healthForeground = drawImg("health_foreground");
this.healthBorder = drawImg("health_border");
this.startTile = startTile;
this.x = x;
this.y = y;
this.intX = (int) x;
this.intY = (int) y;
this.width = texture.getImageWidth();
this.height = texture.getImageHeight();
this.speed1 = speed1;
this.speed = speed;
this.intspeed = speed;
this.grid = grid;
this.health = type.health;
this.inithealth = type.health;
this.hiddenHealth = health;
this.startHealth = health;
this.dir = false;
this.dchosen = false;
this.setx = 0;
this.hurt = 0;
this.panick = 0;
this.deathWish = 0;
this.pdir = -1;
this.myX = x;
this.myY = HEIGHT / 2;
this.right = false;
this.left = false;
this.fade = 0;
this.fir = true;
this.aiType = type.aiType;
this.yOffset = 0;
}
.....
public void AI(int type) {
if(panic > 0)
panic--;
hurt();
speed = speed1;
switch(type) {
case 1:
if(panic > 0) {
if(pickRandom(150, 300) < 10) {
direction = !direction;
}
if(direction) {
if(!right) {
x += speed;
} else {
if(falling < 2)
gravity = 8;
}
} else {
if(!left) {
x -= speed;
} else {
if(falling < 2)
gravity = 8;
}
}
} else {
if(getRange(WIDTH / 2, myX) > 200) {
directionCoolDown++;
if(directionCoolDown > pickRandom(150, 3000)) {
direction = !direction;
directionCoolDown = 0;
}
if(direction) {
if(!right) {
x += speed / 3.2;
} else {
if(falling < 2)
gravity = 8;
}
} else {
if(!left) {
x -= speed / 3.2;
} else {
if(falling < 2)
gravity = 8;
}
}
} else {
if(myX < WIDTH / 2) {
direction = true;
} else {
direction = false;
}
}
}
break;
case 2:
yOffset = -25;
if(!angry) {
pdir = 0;
if(getRange(Player.getX(), myX) < 300) {
hamsterFace = true;
} else {
hamsterFace = false;
}
if(!hamsterFace) {
directionCoolDown++;
if(directionCoolDown > pickRandom(150, 3000)) {
direction = !direction;
directionCoolDown = 0;
}
if(direction) {
if(!right) {
x += speed / 3.2;
} else {
if(falling < 2)
gravity = 8;
}
} else {
if(!left) {
x -= speed / 3.2;
} else {
if(falling < 2)
gravity = 8;
}
}
}
} else {
pdir++;
hamsterFace = false;
if(myX < Player.getX()) {
direction = true;
} else {
direction = false;
}
if(direction) {
if(!right) {
x += speed / 1;
} else {
if(falling < 2)
gravity = 8;
}
} else {
if(!left) {
x -= speed / 1;
} else {
if(falling < 2)
gravity = 8;
}
}
if(getRange(myX, Player.getX()) < 5 && getRange(myY, Player.getY()) < 5) {
hurtPlayer(-2);
direction = !direction;
if(direction) {
if(!right) {
x += speed * 10;
} else {
if(falling < 2)
gravity = 8;
}
} else {
if(!left) {
x -= speed * 10;
} else {
if(falling < 2)
gravity = 8;
}
}
}
}
if(panic > 1) {
angry = true;
} else {
if(pdir > pickRandom(1000,2000)) {
angry = false;
}
}
break;
}
}
.....
(Both classes are in the same package)
EDIT: I fixed the typo....
you have in the Bunny class:
public void AI() {
System.out.println("test");
}
in the Creature class:
public void AI(int type) {
if(panic > 0)
....
so
void AI(int type) and void AI() are NOT the same method (check the signature and how they take different parameters!)
therefore the Bunny class is not overriding anything from the parent class
--
edit:
now that your classes have a method void AI(int type) then we can say that
Bunny override the Creature AI method and everytime you call bunny.AI(f) your bunny method will be called!

JavaFX Animation - Specifics Re. How To Implement

I have some code which does a self-avoiding random walk:
package event_handling;
import java.util.ArrayList;
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class SelfAvoidingRandomWalk extends Application {
int latticeSize;
int scale;
double initialX, initialY;
double currentX, currentY;
ArrayList<Line> lines;
ArrayList<String> moveDirections;
String chosenDirection;
Pane pane;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Random random = new Random();
pane = new Pane();
Button start = new Button("Start");
latticeSize = 16;
scale = 30;
initialX = latticeSize * scale / 2;
initialY = latticeSize * scale / 2;
currentX = initialX;
currentY = initialY;
lines = new ArrayList<>();
moveDirections = new ArrayList<>();
chosenDirection = "";
Line[] horizontalGridLines = new Line[latticeSize + 1];
Line[] verticalGridLines = new Line[latticeSize + 1];
//Draw gridlines
for (int a = 0; a < latticeSize + 1; a++) {
Line l = new Line(0, a * scale, latticeSize * scale, a * scale);
l.setStroke(Color.LIGHTGRAY);
horizontalGridLines[a] = l;
}
for (int a = 0; a < latticeSize + 1; a++) {
Line l = new Line(a * scale, 0, a * scale, latticeSize * scale);
l.setStroke(Color.LIGHTGRAY);
verticalGridLines[a] = l;
}
pane.getChildren().addAll(horizontalGridLines);
pane.getChildren().addAll(verticalGridLines);
BorderPane bPane = new BorderPane();
bPane.setCenter(pane);
bPane.setBottom(start);
BorderPane.setAlignment(start, Pos.CENTER);
start.setOnMouseClicked(e -> {
pane.getChildren().removeAll(lines);
lines.clear();
initialX = latticeSize * scale / 2;
initialY = latticeSize * scale / 2;
currentX = initialX;
currentY = initialY;
while (noNodeTouchesBorders() && validMoveExists()) {
//Check which directions are empty
buildValidMovesList();
//Choose from the available directions
chosenDirection = moveDirections.get(random.nextInt(moveDirections.size()));
//Make the move
makeMove();
//Reset list of possible moves
moveDirections = new ArrayList<>();
}
System.out.println("Finished walk.");
if (noNodeTouchesBorders()) {
System.out.println("Dead end.");
} else {
System.out.println("Reached exit.");
}
});
Scene scene = new Scene(bPane, latticeSize * scale, latticeSize * scale + 30);
primaryStage.setTitle("Self-avoiding Random Walk");
primaryStage.setScene(scene);
primaryStage.show();
}
private boolean noNodeTouchesBorders() {
if (currentX == 0 || currentY == 0 || currentX == latticeSize * scale || currentY == latticeSize * scale) {
return false;
}
return true;
}
private boolean validMoveExists() {
//We have coordinates, and need to check if there are existing lines in the relevant ArrayList with the same endpoint/startpoint.
boolean blocksUp = false;
boolean blocksDown = false;
boolean blocksLeft = false;
boolean blocksRight = false;
//For each line,
for (Line l : lines) {
//Check if this line blocks in some direction
if (blocksUp(l)) {
blocksUp = true;
}
if (blocksDown(l)) {
blocksDown = true;
}
if (blocksLeft(l)) {
blocksLeft = true;
}
if (blocksRight(l)) {
blocksRight = true;
}
}
if (blocksUp && blocksDown && blocksLeft && blocksRight) {
return false;
}
return true;
}
private boolean blocksUp(Line l) {
if ((l.getStartX() == currentX && l.getStartY() == currentY - scale) || (l.getEndX() == currentX && l.getEndY() == currentY - scale)) {
return true;
}
return false;
}
private boolean blocksDown(Line l) {
if ((l.getStartX() == currentX && l.getStartY() == currentY + scale) || (l.getEndX() == currentX && l.getEndY() == currentY + scale)) {
return true;
}
return false;
}
private boolean blocksLeft(Line l) {
if ((l.getStartX() == currentX - scale && l.getStartY() == currentY) || (l.getEndX() == currentX - scale && l.getEndY() == currentY)) {
return true;
}
return false;
}
private boolean blocksRight(Line l) {
if ((l.getStartX() == currentX + scale && l.getStartY() == currentY) || (l.getEndX() == currentX + scale && l.getEndY() == currentY)) {
return true;
}
return false;
}
private void buildValidMovesList() {
moveDirections.add("Up");
moveDirections.add("Down");
moveDirections.add("Left");
moveDirections.add("Right");
for (Line l : lines) {
if (blocksUp(l)) {
moveDirections.remove("Up");
}
if (blocksDown(l)) {
moveDirections.remove("Down");
}
if (blocksLeft(l)) {
moveDirections.remove("Left");
}
if (blocksRight(l)) {
moveDirections.remove("Right");
}
}
}
private void makeMove() {
switch (chosenDirection) {
case "Up" : moveUp(); break;
case "Down" : moveDown(); break;
case "Left" : moveLeft(); break;
case "Right" : moveRight(); break;
}
}
private void moveUp() {
//Create new line
Line l = new Line(currentX, currentY, currentX, currentY - scale);
//Add a new line to the lines ArrayList
lines.add(l);
//Add the new line to the pane
pane.getChildren().add(l);
//Set new currentY
currentY = currentY - scale;
System.out.println("Went up.");
}
private void moveDown() {
//Create new line
Line l = new Line(currentX, currentY, currentX, currentY + scale);
//Add a new line to the lines ArrayList
lines.add(l);
//Add the new line to the pane
pane.getChildren().add(l);
//Set new currentY
currentY = currentY + scale;
System.out.println("Went down.");
}
private void moveLeft() {
//Create new line
Line l = new Line(currentX, currentY, currentX - scale, currentY);
//Add a new line to the lines ArrayList
lines.add(l);
//Add the new line to the pane
pane.getChildren().add(l);
//Set new currentX
currentX = currentX - scale;
System.out.println("Went left.");
}
private void moveRight() {
//Create new line
Line l = new Line(currentX, currentY, currentX + scale, currentY);
//Add a new line to the lines ArrayList
lines.add(l);
//Add the new line to the pane
pane.getChildren().add(l);
//Set new currentX
currentX = currentX + scale;
System.out.println("Went right.");
}
}
Currently, every time I click the Start button, one simulation is done. A complete run of the program, so to say.
My task is to animate this program. So I would like to show each drawn line in its own frame.
I'm not sure how to go about it. I tried and messed up the code. I understand I should probably be using KeyFrames and event handlers... somehow. (I have done simple Animations before, but the code wasn't this complex. There was no need for stepping, or I was using an existing Transition.)
I perceive that my code is a bit weak since the logic is all together with the UI. Not sure if this matters when implementing the animation.
Could you please guide me on how to go about this?
You could wrap your while loop inside an AnimationTimer like this:
start.setOnMouseClicked(e -> {
pane.getChildren().removeAll(lines);
lines.clear();
initialX = latticeSize * scale / 2;
initialY = latticeSize * scale / 2;
currentX = initialX;
currentY = initialY;
AnimationTimer timer = new AnimationTimer() {
long prevTime = 0;
#Override
public void handle(long now) {
// some delay
if ((now - prevTime) < 50_000_000) {
return;
}
prevTime = now;
if (noNodeTouchesBorders() && validMoveExists()) {
// Check which directions are empty
buildValidMovesList();
// Choose from the available directions
chosenDirection = moveDirections.get(random.nextInt(moveDirections.size()));
// Make the move
makeMove();
// Reset list of possible moves
moveDirections = new ArrayList<>();
} else {
stop();
System.out.println("Finished walk.");
if (noNodeTouchesBorders()) {
System.out.println("Dead end.");
} else {
System.out.println("Reached exit.");
}
}
}
};
timer.start();
});

Categories

Resources