Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (483)
Games in Android Showcase (110)
games submitted by our members
Games in WIP (550)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Delta speed and some distance fun.  (Read 819 times)
0 Members and 1 Guest are viewing this topic.
Offline Regenuluz
« Posted 2011-12-23 00:37:53 »

Hey guys,

I've been lurking about on these forums for some time now, and just now decided to register, because no amount of searching and trail and error would yield and answer to me. Sad

So I'm trying to make a simple WormChase game(Like the one in "Killer Game Programming in Java"), but I can't really get the speed of my snake to be independent of how many times I call the snakes tick() function. So I'd love some help on this. Smiley

Also, I'm trying to paint is bodypart of the snake so that they just barely touch each other(I suspect this'll be a problem when trying to adjust move speed), because I want to try and add graphics to the snake(Using sprites). Some help on how to position the body parts would be awesome too(Or rather, how to calculate the new position of the head)

My 2 classes are here:

WormChase.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;

import javax.swing.JFrame;

public class WormChase extends Canvas implements Runnable {
   private static final long serialVersionUID = 1L;

   private static final String GAME_TITLE = "WormChase";

   private static final int WIDTH = 500;
   private static final int HEIGHT = 400;

   private boolean isRunning = false;

   private volatile boolean isPaused = false;

   private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

   private int frames = 0, fps = 0;
   private int ticks = 0, tps = 0;

   private Worm bob;

   private void start() {
      isRunning = true;
      new Thread(this).start();
   }

   public void run() {

      init();

      long lastLoopTime = System.nanoTime();
      double unprocessed = 0;
      double nsPerTick = 1000000000.0 / 10.0;

      long fpsTimer = lastLoopTime;

      while (isRunning) {
         long now = System.nanoTime();
         unprocessed += (now - lastLoopTime) / nsPerTick;
         long delta = now - lastLoopTime;
         lastLoopTime = now;

         while (unprocessed >= 1) {
            unprocessed--;
            tick(delta);
         }

         render();

         if (System.nanoTime() - fpsTimer > 1000000000) {
            fps = frames;
            frames = 0;
            tps = ticks;
            ticks = 0;
            fpsTimer += 1000000000;
         }

         try {
            Thread.sleep(2);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

      System.exit(0);
   }

   private void init() {
      bob = new Worm(WIDTH, HEIGHT, 1f, 40);
   }

   private void tick(long delta) {
      if (!isPaused) {
         // move bob
        bob.tick(delta);
         // Update tick counter
        ticks++;
      }
   }

   private void render() {
      BufferStrategy bs = getBufferStrategy();
      if (bs == null) {
         createBufferStrategy(3);
         return;
      }

      Graphics g = bs.getDrawGraphics();
      g.drawImage(image, 0, 0, WIDTH, HEIGHT, null);

      // draw bob
     bob.render(g);

      // draw fps/ticks
     g.setColor(Color.white);
      g.drawString("fps: " + fps + " ticks:" + tps, 20, 25);

      g.dispose();
      bs.show();

      // Update frame counter
     frames++;
   }

   /**
    * Toggles the state of pause
    */

   public void togglePauseState() {
      isPaused = !isPaused;
   }

   public void testPress(int x, int y) {
      togglePauseState();
   }

   public static void main(String[] args) {
      final WormChase wc = new WormChase();

      wc.setPreferredSize(new Dimension(WIDTH, HEIGHT));
      wc.setMaximumSize(new Dimension(WIDTH, HEIGHT));
      wc.setMinimumSize(new Dimension(WIDTH, HEIGHT));
      wc.setIgnoreRepaint(true);

      JFrame frame = new JFrame(GAME_TITLE);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      Container v = frame.getContentPane();
      v.add(wc);

      frame.pack();

      frame.setFocusable(true);
      frame.requestFocus();

      frame.setResizable(false);
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);

      wc.addMouseListener(new MouseAdapter() {
         public void mousePressed(MouseEvent e) {
//            System.out.println("You clicked, master.");
           wc.testPress(e.getX(), e.getY());
         }
      });

      wc.start();
   }
}


And Worm.java
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;
import java.util.Random;

public class Worm {
   private static Random random = new Random();
   private static final int DOTSIZE = 12;
   private final int MAX_SIZE;

   private final int w, h;

   private Point2D.Double[] wormBody;
   private int curDir;
   private int curSize = 0;
   private int tailPos = -1;
   private int headPos = -1;
   private float speed;

   public Worm(int w, int h, float speed, int max_size) {
      this.w = w;
      this.h = h;
      this.speed = speed;

      MAX_SIZE = max_size;
      wormBody = new Point2D.Double[MAX_SIZE];
      curDir = random.nextInt(360);
   }

   public void render(Graphics g) {
      if (curSize > 0) {
         g.setColor(Color.gray);
         int i = tailPos;
         while (i != headPos) {
            g.fillOval((int)wormBody[i].x, (int)wormBody[i].y, DOTSIZE, DOTSIZE);
            i = (i + 1) % MAX_SIZE;
         }
         
         g.setColor(Color.red);
         g.fillOval((int)wormBody[headPos].x, (int)wormBody[headPos].y, DOTSIZE, DOTSIZE);
      }
   }

   public void tick(long delta) {
      int prevPos = headPos;
      headPos = (headPos + 1) % MAX_SIZE; // Wrapping if needed

      if (curSize == 0) {
         wormBody[headPos] = new Point2D.Double(w / 2, h / 2);
         curSize++;
         tailPos = headPos;
      } else if (curSize == MAX_SIZE) {
         wormBody[headPos] = newPos(prevPos, delta);
         tailPos = (tailPos + 1) % MAX_SIZE;
      } else {
         wormBody[headPos] = newPos(prevPos, delta);
         curSize++;
      }
   }

   /**
    * This is where the new head position is being calculated.
    */

   private Point2D.Double newPos(int prevPos, long delta) {
      double ds = delta / 10000000D * speed;
      double dirInRad = Math.toRadians(curDir);
      double x = wormBody[prevPos].x + Math.cos(dirInRad) * ds + Math.cos(dirInRad) * (DOTSIZE-2);
      double y = wormBody[prevPos].y + Math.sin(dirInRad) * ds + Math.sin(dirInRad) * (DOTSIZE-2);

      if (x < 0) {
         x += w;
      } else if (x >= w) {
         x -= w;
      }

      if (y < 0) {
         y += h;
      } else if (y >= h) {
         y -= h;
      }

      curDir += random.nextInt(90) - 45;

      return new Point2D.Double(x, y);
   }

   public boolean touchedHead(int x, int y) {
      return false;
   }

   public boolean touchedBody(int x, int y) {
      return false;
   }
}


Any help would be awesome! Smiley
Online ra4king

JGO Kernel


Medals: 345
Projects: 2
Exp: 5 years


I'm the King!


« Reply #1 - Posted 2011-12-23 03:44:37 »

What is wrong with your current implementation of a deltaTime?

EDIT: Also, instead of calling cos/sin twice (which hurts performance a lot!), just do: cos/sin(dir) * (ds + DOTSIZE - 2)

Offline Regenuluz
« Reply #2 - Posted 2011-12-23 10:33:11 »

If my ticks per second go up, then the speed of the worm goes op too, without me changing the speed variable. I want the speed to be dependent on the speed var, and not on the number of ticks. Smiley

And good catch about the cos/sin. I thought I had done that already heh, thanks.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Danny02
« Reply #3 - Posted 2011-12-23 13:50:54 »

Ok, I figured out what you are doing wrong.
You oddly combined the two approaches to such a thing(how to handle time in a render/logic loop), either u can choose ,for such a loop, a fixed or dynamically time interval on which the changes get calculated.
Not both as you did.

Quote
What u call ticks, should be fixed time interval. i.e. you want that some logic (or rendering) should be done for example every 10ms.

Dynamically chosen intervals use some sort of time delta for the calculations of the next change.

So why do we have those two different things? The answer is that some loops need different runtime time behavior then others.

Render loops need dynamic time intervals, because the calculations for the rendering can take a lot of time and can vary a lot in the same application. So if u would use a fixed time step you would notice slow downs of the simulated time if the rendering takes longer then your fixed time step.
You mustn't set a lower limit on the time interval of a render loop only a upper, if you want to.

On the other hand a fixed time interval is quite useful for logic or physic processing, because a fixed interval makes the outcome more predictable and stable.

To come back to your problem, the simple answer is: replace
1  
tick(delta);

with
1  
tick(nsPerTick);


the long:
either choose a fixed interval, then u would get rid of the delta in your update method of the worm and use a constant.

Or you get rid of all your tick code and just use the delta.


also some suggestions:
  • get rid of the System.exit(0); in your thread
  • I like your general coding style, only thing I saw was the capitalized MAX_SIZE(it is no constant)
  • nsPerTick on the other hand is
  • try to not couple presentation with logic, the game playfield size shouldn't be linked to the size of your window(the logic shouldn't worry about how big the window is, the window should worry about how big the game wants to be)
  • do the decoupling even further, one class for rendering one for data von for logic (MVC pattern)
Offline Regenuluz
« Reply #4 - Posted 2011-12-23 14:29:40 »

Haha, I think I've gotten something mixed up, eh? Tongue Thanks for clearing up the tick() thing! Smiley Now my worms can actually move with different speeds and stuff. (Though it required that I removed the thing that made each circle just barely touch the other one. I'll have to figure out another way of doing that, so it wont affect the speed. I'm thinking that using a circular buffer isn't the way to go - I'll probably have to move each circle, everytime)

I removed the System.exit(0) and made max_size lowercase. It started out as being a constant, that's why it was uppercase. Smiley

I'm not entirely sure I know what you mean with:
Quote
try to not couple presentation with logic, the game playfield size shouldn't be linked to the size of your window(the logic shouldn't worry about how big the window is, the window should worry about how big the game wants to be)

And I have plans for making a Screen class for handling rendering. Smiley
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CopyableCougar4 (17 views)
2014-08-22 19:31:30

atombrot (28 views)
2014-08-19 09:29:53

Tekkerue (25 views)
2014-08-16 06:45:27

Tekkerue (23 views)
2014-08-16 06:22:17

Tekkerue (15 views)
2014-08-16 06:20:21

Tekkerue (22 views)
2014-08-16 06:12:11

Rayexar (61 views)
2014-08-11 02:49:23

BurntPizza (39 views)
2014-08-09 21:09:32

BurntPizza (31 views)
2014-08-08 02:01:56

Norakomi (38 views)
2014-08-06 19:49:38
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!