I know there already are many similar questions and answers here, I've done searching some and I think I should ask this as a new question as I still can't find the right answer for me.
So, I write a simple card game, and I got 90% of my knowledge from the book "Beginning Android Games 2nd edition" by Mario Zechner, I wonder if some of you have read it. I follow the guide during writing my game, I even use the framework he provides in the book (Mr.Nom game). I've asked in his forum, but no response, the forum is not too active anyway.
The book provide framework where there are method to draw Pixmap, draw line, etc.. all directly from code, so I never touch the layout, I don't use XML either.
The book uses model similar to MVC (model view controller), separating the world and the game. The presentation and the manipulation layerd. It separates each UI according the game state: Ready, paused, running, gameOver.
Now I am stuck at some code as I want to change my button image. I provide 2 image (Unpressed and Pressed), at first in so called (RunningUI) part, I draw Unpressed image. and simply when user touch the button (touch down only) I want to change the image to pressed image. And return back to unpressed image again after the touch is released.
private void drawRunningUI() {
Graphics g = game.getGraphics();
g.drawPixmap(Assets.buttonUnpressed, 70, 200);
}
The updateRunning code is here:
private void updateRunning(List<TouchEvent> touchEvents, float deltaTime) {
Graphics g = game.getGraphics();
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_UP) {
if (event.x < 64 && event.y < 64) {
if (Settings.soundEnabled)
Assets.click.play(1);
state = GameState.Paused;
return;
}
}
if (event.type == TouchEvent.TOUCH_DOWN) {
if (event.x >= 120 && event.x <= 180 && event.y >= 250 && event.y <= 380) {
Assets.click.play(1);
g.drawPixmap(Assets.buttonPressed, 70, 200); <-- nothing happened
}
}
}
world.update(deltaTime);
}
I modified "some here and there", but still I don't get the result I want.
Although I don't know this framework and can only surmise how the canvas drawing is made, I suspect drawRunningUI() is called immediately after updateRunning() and "overwrites" the previous draw with Assets.buttonPressed (depends on how invalidation / redraw is handled).
Anyway, you should only draw in one place, and it then gets a lot easier. In your event handling code you should update a UI state which you would then use to draw the UI. Something like this:
private void updateRunning(List<TouchEvent> touchEvents, float deltaTime) {
int len = touchEvents.size();
for(int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if(event.x < 64 && event.y < 64) {
if(event.type == TouchEvent.TOUCH_UP) {
// ...
}
else if (event.x >= 120 && event.x <= 180 && event.y >= 250 && event.y <= 380) {
switch(event.type) {
case TouchEvent.TOUCH_UP:
uiState.updateButtonState(ButtonState.RELEASED);
break;
case TouchEvent.TOUCH_DOWN:
Assets.click.play(1);
uiState.updateButtonState(ButtonState.PRESSED);
break;
default:
// nothing to do
}
}
}
world.update(deltaTime);
// I suppose an invalidation has happened / a redraw will happen next
}
private void drawRunningUI() {
Graphics g = game.getGraphics();
g.drawPixmap(uiState.getButtonState() == ButtonState.PRESSED ? Assets. buttonPressed : Assets.buttonUnpressed,70,200);
}
This won't be enough but should illustrate the idea.
Related
I am trying to make a game and am almost done with the code. But I can't make my rectangle randomly fall down from my screen.
I am coding a car game that is supposed to dodge the other cars. But is not working. can someone help me?
int carx = 200;
int cary = 0;
float speedx = 0;
float speedy = 0;
float accy = 0.1;
color rod = color(255, 0, 0);
color vit = color(255);
final int START_STATE = 1;
final int PLAY_STATE = 2;
final int GAMEOVER_STATE = 3;
final int RESTART = 4;
int state = START_STATE;
void setup() {
size(400, 700);
}
void draw() {
switch (state) {
case START_STATE:
drawStart();
break;
case PLAY_STATE:
drawPlay();
break;
case GAMEOVER_STATE:
drawGameOver();
case RESTART:
drawrestart();
}
}
void init() {
carx = 200;
cary = 0;
speedx = 0;
speedy = 0;
accy = 0.1;
}
void drawStart() {
background(0);
textAlign(CENTER, CENTER);
textSize(16);
text("Click the mouse button to start the game", width / 2, height / 2);
if (mousePressed) {
state = PLAY_STATE;
}
}
void drawGameOver() {
textAlign(CENTER, CENTER);
textSize(20);
text("you have crashed your car", width / 2, height / 2);
if (mousePressed) {
state = PLAY_STATE;
init();
}
}
void drawrestart() {
textAlign(CENTER, CENTER);
textSize(15);
text("press mouse to restart", 200, 400);
if (keyPressed) {
state = RESTART;
}
}
void drawPlay() {
background(0);
if (get(carx, cary) == vit) {
speedy = -1 * speedy;
}
fill(rod);
rect(carx, cary, 50, 30);
if (get(mouseX, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
fill(#FFFFFF);
rect(mouseX, 600, 30, 50);
carx += speedx;
cary += speedy;
speedy += accy;
}
The code you have at the moment only has one rectangle fall down from the top for each 'round' of the game. I'm not sure if you wanted to have multiple blocks falling; I think that would be a good next step.
For now, here's a simple hack which will cause the block to fall from a random position each time, like you requested.
At the very start of your code, outside of the functions, place:
boolean randomise;
Then, within void init() you should add:
randomise = true;
Finally, add this section into drawPlay(), right at the start of the function:
if (randomise){
carx = int(random(width-50));
randomise = false;
}
Note that a new random x co-ordinate will only generate every time you set the boolean 'randomise' to true again. So if you generate a new iteration with more than one block falling inside the drawPlay() function, you should bear this in mind and adjust the code accordingly.
------- [EDIT] -------
Hi, glad that this helped.
I've actually noticed another little issue which I will help you fix.
Currently, you are checking at mouseX to see whether there has been a collision. This works (mostly), but if the right side of the player's white car drives through the left edge of a red falling block, then the game continues as though nothing has happened. What should occur is that the game is over because a collision is detected. You want to find out if any part of the two shapes have overlapped.
To do this, you should alter the code like so. In drawPlay(), replace:
if (get(mouseX, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
with:
if (get(mouseX, 600) == color(255, 0, 0) || get(mouseX + 30, 600) == color(255, 0, 0)) {
state = GAMEOVER_STATE;
}
This is an OR statement - checking whether either side of the player's car has collided. This way, every time they bump into each other, the game will end.
Now for your question: how to add multiple cars?
There are a few ways you could go about this, but I'll show you the most straightforward.
In drawPlay(), you want to add this little if statement:
if (cary > height){
init();
}
Basically what we're doing here is checking if the previous red block/car has fallen off the bottom of the sketch. If it has, i.e. if the red block's y co-ordinate is larger than the height of the whole sketch, we call init() again. This will reset everything, including making randomise true. Once randomise is true, the previous code you added will select a random start point for the block, and it will fall all over again.
I hope you've understood everything I explained - whilst my code will fix your problem, the best way to learn is to try to solve things by yourself. Check out Dan Shiffman's videos on YouTube. These are a great starting place to get to grips with Processing - hopefully you'll be more confident writing your own code after following along with his examples :)
Use random to generate a random x coordinate:
carx = (int)random(0, width-50);
Reset the position and the speed of the care, once the car reached to bottom of the window:
void drawPlay() {
if (cary > height) {
carx = (int)random(0, width-50);
cary = 0;
speedy = 0;
}
// [...]
I want to have the panda (which the user moves with left and right arrow keys) collide with a candy image which is falling from the top left of the screen. When the 2 images collide, it would say "YOU TOUCHED THE CANDY" on the screen and the console. But when the panda and candy touch, nothing happens.
Here is the code:
PImage candy, panda;
int pandaX = 800;
int pandaY = 790;
int candySpeed = 20;
int yDirCandy = 1;
int yPosCandy = 10;
int xPosCandy = 20;
void setup() {
candy = loadImage("goodCandy1.png");
panda = loadImage("panda.png");
}
void loadStuff() {
image(panda, pandaX, pandaY, 80, 112);
text("Lives: " + lives, 1400, 70);
image(candy, xPosCandy, yPosCandy, 187, 121);
yPosCandy = yPosCandy + (candySpeed * yDirCandy);
touchCandy(pandaX, pandaY, yPosCandy, xPosCandy);
}
void keyPressed() {
if (key==CODED) {
if (keyCode==LEFT) {
pandaX = pandaX-20;
}
if (keyCode==RIGHT) {
pandaX = pandaX+20;
}
if (pandaX<=10) {
pandaX=10;
lives = lives -1;
}
if (pandaX>=1500) {
pandaX=1500;
}
}
}
void touchCandy(int xPosCandy, int yPosCandy, int pandaX, int pandaY) {
if (pandaX==xPosCandy && pandaY==yPosCandy ) {
text("YOU TOUCHED THE CANDY!", width/2, height/2);
print("YOU TOUCHED THE CANDY!");
}
}
I tried doing if (pandaX==xPosCandy && pandaY==yPosCandy) But it didn't work. I also tried doing if (pandaX>xPosCandy && pandaY<yPosCandy) but it says YOU TOUCHED THE CANDY too soon.
Any help would be appreciated, and let me know if you need more code. Thanks!
Look at this line:
if (pandaX==xPosCandy && pandaY==yPosCandy)
This will only be true if the panda and the candy are in exactly the same position. This is pretty unlikely.
Similarly, look at this line:
if (pandaX>xPosCandy && pandaY<yPosCandy)
This will only be true if the panda is to the right and above the candy, which is not what you want.
Instead, you need to check whether they're overlapping. You'd do this by checking each side, like this:
if(rectOneRight > rectTwoLeft && rectOneLeft < rectTwoRight && rectOneBottom > rectTwoTop && rectOneTop < rectTwoBottom){
//collision
}
Shameless self-promotion: I've written a tutorial on collision detection in Processing (including rectangle-rectangle collision) available here.
I am making a racing game using Libgdx. I want to touch the half right side of screen to speed up, at the same time without removing previous touch point touch again another on the left side of the screen to fire a shot. I am unable to detect later touch points.
I have searched and get Gdx.input.isTouched(int index) method, but cannot determin how to use it. My screen touch code is:
if(Gdx.input.isTouched(0) && world.heroCar.state != HeroCar.HERO_STATE_HIT){
guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
if (OverlapTester.pointInRectangle(rightScreenBounds, touchPoint.x, touchPoint.y)) {
world.heroCar.state = HeroCar.HERO_STATE_FASTRUN;
world.heroCar.velocity.y = HeroCar.HERO_STATE_FASTRUN_VELOCITY;
}
} else {
world.heroCar.velocity.y = HeroCar.HERO_RUN_VELOCITY;
}
if (Gdx.input.isTouched(1)) {
guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
if (OverlapTester.pointInRectangle(leftScreenBounds, touchPoint.x, touchPoint.y)) {
world.shot();
}
}
You'll want to use the Gdx.input.getX(int index) method. The integer index parameter represents the ID of an active pointer. To correctly use this, you will want to iterate through all the possible pointers (in case two people have 20 fingers on the tablet?).
Something like this:
boolean fire = false;
boolean fast = false;
final int fireAreaMax = 120; // This should be scaled to the size of the screen?
final int fastAreaMin = Gdx.graphics.getWidth() - 120;
for (int i = 0; i < 20; i++) { // 20 is max number of touch points
if (Gdx.input.isTouched(i)) {
final int iX = Gdx.input.getX(i);
fire = fire || (iX < fireAreaMax); // Touch coordinates are in screen space
fast = fast || (iX > fastAreaMin);
}
}
if (fast) {
// speed things up
} else {
// slow things down
}
if (fire) {
// Fire!
}
An alternative approach is to setup an InputProcessor to get input events (instead of "polling" the input as the above example). And when a pointer enters one of the areas, you would have to track that pointer's state (so you could clear it if it left).
The problem I'm having is with my render loop. My application is a series of 'Tile' objects each with an x and y coordinate and image. When the program starts it creates a 10x10 grid of these tiles on screen. However, not all the squares can be seen at the same time, so you can use the arrow keys to pan around them. When the key is pressed it uses a for loop to cycle through all the currently rendered tile (stored in an ArrayList) and shifts them all 16 in the appropriate direction. The problem is some of the tiles flicker. I can see when scrolling that one half of the screen doesn't move in time to be rendered in the right spot, making a black gap between that and the other half of the tiles. how do I ensure that all tiles are moved before rendering?
render function from my Core class
public static void render()
{
while(true)
{
Graphics g = buffer.getDrawGraphics();
try
{
g.setColor(Color.black);
g.fillRect(0, 0, 1280, 720);
if(renderQueue != null)
{
for(int i = 0; i<renderQueue.size(); i++)
{
Tile t = renderQueue.get(i);
g.drawImage(t.getImage(), t.getX(), t.getY(), null);
}
}
if(!buffer.contentsLost())
{
buffer.show();
}
}
finally
{
if(g != null)
{
g.dispose();
}
}
}
}
And here's the movement update function from the Input class
public void keyPressed(KeyEvent ke)
{
int e = ke.getKeyCode();
switch(e)
{
case 38://up
if(scrollY > 0)
{
scrollY -= 16;
for(int i = 0; i<Core.renderQueue.size(); i++)
{
Core.renderQueue.get(i).incrementY(16);
}
}
break;
case 40://down
if(scrollY < 560)
{
scrollY += 16;
for(int i = 0; i<Core.renderQueue.size(); i++)
{
Core.renderQueue.get(i).incrementY(-16);
}
}
break;
case 37://right
if(scrollX < 0)
{
scrollX += 16;
for(int i = 0; i<Core.renderQueue.size(); i++)
{
Core.renderQueue.get(i).incrementX(16);
}
}
break;
case 39://left
if(scrollX > 0)
{
scrollX -= 16;
for(int i = 0; i<Core.renderQueue.size(); i++)
{
Core.renderQueue.get(i).incrementX(-16);
}
}
break;
}
Thanks in advance!
It sounds like the tiles are being rendered while the coordinates for some of the tiles still have to be changed by Input.keyPressed. You could fix that by directly using scrollX and scrollY to draw the tile images in Core.render, instead of changing the coordinates for each of the tiles. If you copy the scroll values to two local variables at the begin of the while loop in render, the same values will be used for each tile.
Another option is to create a new list with tiles that have the modified coordinates (you could use the images from the current list). When the new list is complete, you could set a flag like newRenderQueue which will be picked up in render. When a new iteration of the while loop in render starts, you can replace the render queue with the new list and reset the flag.
P.S. Welcome to Stack Overflow! As Andrew Thompson already mentioned, it's very helpful to provide a complete example of your problem. This way people can quickly investigate the issue and provide (hopefully useful) advice... ;-)
I'm wondering how I can move an image after it has been drawn?
Heres my code for drawing the image:
public int probeX = 500;
public int Minerals = 400;
public int drawProbeA, drawProbe = 0;
public void init() {
// Images Call
probe = getImage(getDocumentBase(), "image/probe.png");
}
public void paint(Graphics g) {
if (drawProbe == 1) {
for (int k = 0; k < drawProbeA; k++) {
g.drawImage(probe, probeX, 474, 50, 50, this);
probeX += 50;
}
probeX = 500;
}
}
public boolean mouseDown(Event e, int x, int y) {
// Clicking on the probe icon
if (x > 1068 && x < 1119 && y > 785 && y < 832 && onNexus == 1
&& Minerals >= 50) {
drawProbeA += 1;
drawProbe = 1;
Minerals -= 50;
}
return true;
}
How can I make it so that after the images are drawn, that hitting an icon will cause the image to be auto moved down the y-axis (like 50 pixels)? Basically, like sliding the image down with an animation? And then stop and then move back up to the orginal spot.
I am using an Applet and would like the animation to loop repeatedly. Thanks.
You need to have a global variable, or another variable somewhere, that indicates that...
The image needs to move
How far in the Y direction it has moved already
Which direction it is going (up or down)
When you have this, you need to add code to your paint() method to draw the image in the correct spot.
You would also need a Timer or Thread that will tell the component to repaint() every few milliseconds, and change your global variables so that it will repaint it lower/higher.
So, as a bit of an example, you might have some global variables like this...
int yPosition = 0;
boolean goingDown = true;
When you need to start your animation, start a Timer that calls the following over and over...
if (goingDown == true){
// if we've gone down 50 pixels, start going up again
if (yPosition <= 0){
goingDown = false;
yPosition++;
}
else {
yPosition--; // move it down 1 pixel
}
}
else {
// if we're going up and we reach 0, go down again
if (yPosition >= 50){
goingDown = true;
yPosition--;
}
else {
yPosition++; // move it up1 pixel
}
}
component.repaint(); // this will call the paint() method
Not your paint method just need to draw your image at the different position. Just change the g.drawImage(probe,probeX,474,50,50,this); like to include the yPosition...
g.drawImage(probe,probeX,474+yPosition,50,50,this);
This should at least point you in the right direction.