I am making a simple 3D multiplayer game. In the server side I'm making all the calculations for each client/player in-game based on player position and look direction (both should be updated from user input), and then I want to send all resulting triangles/polygons to the client in order to draw to the screen in each frame. I want the client to send to the server in each frame the buttons pressed on the keyboard (so if W and Space are held down, their flags are set to true and sent that way to the server), and also the change in mouse position. Based on that input I change the position of the player in server-side and the look direction of the camera. The way I have it set up now is that both the client and server are infinite loops without delay.
The server goes something like this in each frame/iteration of the loop:
Send a message to client that tells it to update mouse (more about that below)
Double player speed for the frame if a specific key is pressed
Update look direction based of dx and dy (how much the mouse moved since last frame)
Calculate next position of player based on which keys are pressed
Collision detection, push back player if inside an object or wall
Project all scene triangles into 2D based on look direction (camera matrix)
Send a message to client that tells it to clear all triangles from list
Send the data of each 2D-projected triangle (coordinates and color)
And the client does this in each frame:
Send which keys are currently pressed and which are not
Call repaint() method:
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
ArrayList<Triangle> trianglesToDraw = new ArrayList<>(updatedTriangles);
trianglesToDraw.removeIf(Objects::isNull);
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
for (Triangle t : trianglesToDraw)
{
g.setColor(t.color);
int[] xValues = {(int) t.points[0].x, (int) t.points[1].x, (int) t.points[2].x};
int[] yValues = {(int) t.points[0].y, (int) t.points[1].y, (int) t.points[2].y};
g.fillPolygon(new Polygon(xValues, yValues, 3));
}
}
It is worth noting that each side has a separate thread which is responsible for handling messages from the other.
In the server side:
public void handleClientMessage(String message)
{
if (message.startsWith("keys: "))
{
String[] keyInfo = message.replaceAll(",", "").split(" ");
for (int i = 0; i < keysPressed.length; i++)
{
keysPressed[i] = Boolean.parseBoolean(keyInfo[i + 1]);
}
}
else if (message.startsWith("mouse_update: "))
{
String[] mouseInfo = message.split(" ");
try
{
mouseDX = Float.parseFloat(mouseInfo[1]);
mouseDY = Float.parseFloat(mouseInfo[2]);
}
catch (NumberFormatException e)
{
mouseDX = 0;
mouseDY = 0;
}
}
In the client side:
public void handleMessageFromServer(String message) throws IOException
{
if (message.contains("Update Mouse"))
{
mousePosition = MouseInfo.getPointerInfo().getLocation();
int centerX = screenWidth / 2;
int centerY = screenHeight / 2;
robot.mouseMove(centerX, centerY);
float dx = mousePosition.x - centerX;
float dy = mousePosition.y - centerY;
sendMessage("mouse_update: " + dx + " " + dy);
}
else if (message.equals("clear triangles"))
{
updatedTriangles.clear();
}
else if (message.startsWith("triangle"))
{
String[] triangleInfo = message.split(" ");
Triangle t = new Triangle(
new Vector(Float.parseFloat(triangleInfo[1]), Float.parseFloat(triangleInfo[2]), Float.parseFloat(triangleInfo[3])),
new Vector(Float.parseFloat(triangleInfo[4]), Float.parseFloat(triangleInfo[5]), Float.parseFloat(triangleInfo[6])),
new Vector(Float.parseFloat(triangleInfo[7]), Float.parseFloat(triangleInfo[8]), Float.parseFloat(triangleInfo[9])),
new Color(Integer.parseInt(triangleInfo[10]), Integer.parseInt(triangleInfo[11]), Integer.parseInt(triangleInfo[12]))
);
updatedTriangles.add(t);
}
}
Now my layout is really bad for many reasons:
I can barely move the camera because I expect the client to send updated dx and dy and the server to receive it and update it before it makes the new look direction calculation (which is really unrealistic considering the calculation happends only 2-3 O(1) calculations after the original request it sends to the client). What actually happens is that it's almost always 0.0 for both because it won't update in time.
The screen keeps flickering because the client isn't synced with the server so it updates the screen without receiving all triangles from the server and clears the screen before.
All of the important calculations are performed in server-side, which is something that makes sense in my mind, but it also means that the more clients there will be the slower it will run for everyone since the server has limited resources to calculate everything.
There are probably more problems that I can't think about at the moment but these are the main ones.
Anyways, I need help to figure out a better layout that will at the very least solve some of these problems and prevent some more future ones. I just don't have enough experience with this so I don't really know the best practices in server-client programming.
Related
I have a number of TextButtons I can drag, checking if they overlap an Image when I let go. Currently what I'm experiencing is either a particular object will detect collision located anywhere on screen, or it will never collide. Note that I'm not using the native DragAndDrop class, but have adapted a parallel implementation from a book.
Given that my TextButtons move when I drag them, I think the following function is updating the (x,y) of the object:
public void touchDragged(InputEvent event, float eventOffsetX, float eventOffsetY, int pointer)
{
float deltaX = eventOffsetX - grabOffsetX;
float deltaY = eventOffsetY - grabOffsetY;
a.moveBy(deltaX, deltaY);
}
Is that correct that the Actor a's x, y change due to moveBy? Because my latter collision detection - where I examine the dragged objects coordinates - reports the same x,y coordinates for the dragged object no matter where I release it. Here's the log for releasing the object from two different locations on the screen:
Does (626.8995, 393.1301)(923.8995, 393.1301)(923.8995, 499.1301)(626.8995, 499.1301) fit into (610.0, 256.0)(990.0, 256.0)(990.0, 677.0)(610.0, 677.0)?
Does (626.8995, 393.1301)(923.8995, 393.1301)(923.8995, 499.1301)(626.8995, 499.1301) fit into (610.0, 256.0)(990.0, 256.0)(990.0, 677.0)(610.0, 677.0)?
and here's the collision detection and sys out generating those log messages:
//this is called by the dragged obj, a, on touchUp() against each of the targets
public boolean overlaps(Actor other)
{
//a is the first, dragged object, other is the target
if (poly1 == null)
poly1 = getPolygon(a);
Polygon poly2 = getPolygon(other);
float[] p1v = poly1.getVertices();
StringBuilder sb = new StringBuilder();
for (int i=0; i<p1v.length-1; i+=2)
sb.append("(").append(p1v[i]).append(", ").append(p1v[i+1]).append(")");
float[] p2v = poly2.getVertices();
StringBuilder sb2 = new StringBuilder();
for (int i=0; i<p2v.length-1; i+=2)
sb2.append("(").append(p2v[i]).append(", ").append(p2v[i+1]).append(")");
System.out.println("Does " + sb + " fit into " + sb2 + "?");
// initial test to improve performance
if ( !poly1.getBoundingRectangle().overlaps(poly2.getBoundingRectangle()) )
return false;
return Intersector.overlapConvexPolygons( poly1, poly2 );
}
public Polygon getPolygon(Actor a) {
Polygon p = new Polygon();
// float[] vertex = { a.getOriginX(), a.getOriginY(), a.getOriginX(), a.getY(), a.getX(), a.getY(), a.getX(), a.getOriginY() };//new float[8];
// float[] vertex = { 0, 0, a.getWidth(), 0, a.getWidth(), a.getHeight(), 0, a.getHeight()};
float[] vertex = { a.getX(), a.getY(), (a.getX() + a.getWidth()), a.getY(), (a.getX() + a.getWidth()), (a.getY() + a.getHeight()), a.getX(), (a.getY() + a.getHeight())};
p.setVertices(vertex);
// p.setPosition(a.getX(), a.getY());
//p.setOrigin(a.getOriginX(), a.getOriginY());
return p;
}
There are a HORDE of collision detection posts already on StackOverflow, and they helped some in showing me how to form valid polygons. Perhaps the 3rd party drag and drop is why I'm not finding my answer in the wealth of knowledge out there, but I'm leaning towards some annoying mistake I'm overlooking.
Score another one for logic failure. Originally I thought I'd just be saving the dimensions of the dragged polygon when I decided to cache it, thinking it would save a few polygon creation steps as it was checked against a number of potential targets. Later, as I kept reworking what values to feed the polygon vertices, I tied in the location of the polygon as well. So it was just caching the first place I dragged it to, and using that every time I dragged it somewhere.
Thanks for the comment, it helped me move past thinking I wasn't understanding the classes. I'm doubtful this particular mistake/resolution will ever be of use to someone else, and would be very understanding if this post is removed.
I am a new coder on processing, because of this reason please be gentle.
Normally my code is more longer and complex but I wrote a simple code for you, which I can adapt on my code:
int speed = 1;
int x, z = 50;
void setup() {
size(400, 400, P3D);
}
void draw() {
background(0);
noStroke();
lights();
translate(x, height/2, z);
fill(255,0,0);
sphere(25);
if (x > width -50)
noLoop();
x += speed;
}
So, like you see, sphere starts with color of red and it reaches end of the window. I want to change it's color from red to white and it should take 30 sec. to reach end of the window. However I don't know how. If you help me I will be happy.
Note: I tried lerpColor function but didn't help me.
Math is the secret. Is often is.
There are a couple things you'll need to keep track of to accomplish this: the time it takes to cross the screen (you said 30 seconds), the speed of the sphere, the speed at which the color changes.
Before we start, I suggest that you use float for your variables which are positions and speeds. Integers will do the job, but at some point when you want precision you may regret not using floats or a similar type.
There are 2 ways to deal with changes over time: you can calculate time and draw what needs to be drawn where it's supposed to be, or calculate how many frames will be drawn in a certain amount of time and move things accordingly. The first technique has the advantage of being able to draw things where they are supposed to be even if the system is laggy (Processing will lower the framerate if it's not able to respect it), while the second is often easier to work with. We'll go with the framerate technique, as this is not supposed to be complicated and because most programs won't need so much resource that it'll lower the framerate.
The framerate, in Processing, is also the rate at which the main loop (the draw() loop) run. So we'll choose a framerate which will let us calculate the speed of the sphere and the speed at which the color change. The rest is just watching it move.
Here's your example, but modified so it works approximately as you told:
float speed;
float x, z = 50;
float greenBlueStrength = 0;
float colorFadeRate = 1;
int fadeTimeInFrames;
void setup() {
size(400, 400, P3D);
frameRate(60); // 60 is the default framerate per second
// so 30 seconds == (30*60) == 1800 frames
// so you must have the speed to match
fadeTimeInFrames = 60 * 30;
speed = (width - 50) / (float)fadeTimeInFrames;
colorFadeRate = 255 / (float)fadeTimeInFrames;
println(colorFadeRate);
}
void draw() {
background(0);
textSize(30);
text((millis()/1000) + " s. // color: " + (int)greenBlueStrength, 20, 50);
// this is just to keep track of changes while they happen
noStroke();
lights();
translate(x, height/2, z);
fill(255, greenBlueStrength, greenBlueStrength);
sphere(25);
if (x > width -50) {
noLoop();
} // no actual change, but use brackets anyway, it's easier to read
// updating what needs to be updated
x += speed;
greenBlueStrength += colorFadeRate;
}
I'll hang around so don't hesitate if you have questions.
Have fun!
I think something like that would work:
int r=255,b=255,g=255;
...
void draw(){
...
int percent=x/width*100;
fill(r,b*percent,g*percent)
sphere(25)
...
}
so the sphere would be red only on the left of the screen and white on the right
this is for homework.
We have to create a JavaFX application, complying to the MVP principle, that shows a static sine-wave, with sliders to control the properties of said sine-wave.
These sliders are the amplitude, frequency, phase, and zoom. They're bound, through the presenter, to properties in my model that make up the sine-wave. These then have listeners to update the model on changes.
For drawing my sine-wave, I chose a polyline and I calculate the X and Y coordinates for each point to a observable list in my model:
for (double x = -360; x < 360; x++) {
data.add(x);
data.add(Math.sin(frequency.doubleValue() * x + phase.doubleValue()) * amplitude.doubleValue());
}
Then I reach this dataset to my view through the presenter where I give each point to my polyline:
public void setPoints(ObservableList<Double> list) {
functionLine.getPoints().clear();
functionLine.getPoints().addAll(list);
double temp;
for(int i = 0;i<functionLine.getPoints().size();i++) {
//separate x from y coordinates
if (i % 2 == 0) {
temp = functionLine.getPoints().get(i);
functionLine.getPoints().set(i, temp + (display.getWidth() / 2)); // + displaywidth/2 to get it to the center of the graph
} else {
temp = functionLine.getPoints().get(i);
functionLine.getPoints().set(i, temp + ((display.getHeight() / 2))); //same as above
}
}
}
This also doesn't perform very well because of the for-loop and the interface is laggy, but that's not why I am here.
This is what is currently looks like. The polyline and graph (two lines) are located in its own pane:
Now I have tried to also add zoom to this without increasing the width of the actual line, but I can't figure out how to properly scale around the center of my graph. Obviously I have to transform the coordinates of each point, but I don't know how. I have tried several things but it doesn't achieve what I want.
Feels like something I should be able to do on my own, but I can't apparently.
Any help would be appreciated.
I have three global variables:
private PhysicsActor blade;
private PhysicsActor blades;
private ArrayList<PhysicsActors> blades;
I created an actor object from a class I created for my game.
blade = new PhysicsActor();
blade.storeAnimation( "", exTex );
blade.setOriginCenter();
blade.setEllipseBoundary();
blade.setMaxSpeed(50);
blade.setDeceleration(50);
bladesList = new ArrayList<PhysicsActor>();
for (int i = 0; i < 3 ; i++)
{
float xCoord = randomFloatGenerator(425, 50);
float yCoord = randomFloatGenerator(mapHeight - 200, 275);
blades = blade.clone();
blades.setPosition(xCoord, yCoord);
mainStage.addActor(blades);
bladesList.add(blades);
}
The problem is not that they do not spawn. It is that when I call for them to rotate while my game is running in my update(float dt) method, only one of them is rotating:
public void update(float dt)
{
// rotate the blade 70 degrees
blades.rotateBy(70);
// rest of code etc
}
Here is an image to help visualize
I know that this is happening because I am only rotating the blades actor. What I want to do is have them all rotate from the ArrayList. I do not know how to get them from the list however. I have tried bladesList.get(i) using a for loop and a couple other ways I saw online but it would not work. Any tips or instructions for me?
Also, I will post more code to clarify anything confusing if requested.
You can try this
for (PhysicsActor blade : bladesList) {
blade.rotateBy(70);
}
this will make all the blades in your list rotate by 70. Given you can access the array from where you are calling it.
I'm trying to create a simple Android game, a 2D action shooter which has 2 control sticks (circles) on the screen, the left one is movement control and the right one weapon control. Direction is being controlled by the position of your thumb relative to the circle’s center.
I've been following a tutorial on this site: http://www.kilobolt.com/day-7-creating-an-android-game-from-start-to-finish.html but it only gave me the base to work on. I have no programming experience so I'm quite lost now.
I got the movement working only on TOUCH_DOWN event, the hero moves to about where it should but to change direction I have to lift my thumb and touch the circle again. TOUCH_DRAGGED (or ACTION_MOVE) is broken because if I drag my finger across the circle the character moves really fast. I guess the problem is too many touch events are being handled, but I have no idea how to fix it.
How can I change this so that I can drag my thumb around the circle and the hero will change its direction instantly, and keep its speed constant all the time? Also the speed should be the same no matter how close or far from the center of the circle you touch.
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.type == TouchEvent.TOUCH_DOWN || event.type == TouchEvent.TOUCH_DRAGGED) {
if (inBounds(event, 50, 300, 150, 150, 'c')) {
ctrl.setX(event.x);
ctrl.setY(event.y);
ctrl.direction();
hero.move(ctrl.getDirX(), ctrl.getDirY());
}
}
if (event.type == TouchEvent.TOUCH_UP) {
if (inBounds(event, 0, 0, 35, 35,'r')) {
pause();
}
hero.stopMove();
}
}
hero.update();
The movement: hero's speedX and speedY are added to hero's centerX and centerY on every hero.update() call.
public void move(float x, float y) {
speedX += x * MOVESPEED;
speedY += y * MOVESPEED;
}
This method that handles the x & y speed. Found it here at stackoverflow, and because touch_down event is working ok, I guess it's doing it's job. Although I feel it's not exactly how it's supposed to be. L_STICK_C is a constant with values of 100 and 350 (center of the circle).
public void direction() {
dir.x = x - L_STICK_C.x;
dir.y = y - L_STICK_C.y;
double hyp = Math.sqrt(dir.x * dir.x + dir.y * dir.y);
dir.x = (float) (dir.x / hyp);
dir.y = (float) (dir.y / hyp);
}
I suggest you look into some game programming tutorials. You usually would not move the character directly from the touch input. You'd set a game state variable once a game loop which would correspond to the position of your thumb inputs. Then you'd only update the hero once per game loop based on those inputs. This lets allows you to keep the game input control code, and hero code separate, and makes it re-usable for other parts of your game.
EDIT:
Based on your code, every time you drag your finger, you generate a bunch of dragged events. So you are adding onto your characters speed for each event. You should probably just be looking at the distance to center and x / y of the input on the last touch event, not all of them.