Horizontal only touchpad in libgdx - libgdx

Im trying to modify the libgdx Touchpad class to only be horizontal movement and not a full circle. So something like this:
But the touch is messed up and doesnt stop the movement within the bounds of the touchpad like it does for the round version. Heres my version, any help on where Im going wrong would be appreciated:
public class HorizontalTouchpad extends Widget {
private HorizontalTouchpadStyle style;
boolean touched;
boolean resetOnTouchUp = true;
private float deadzoneWidth;
private final Rectangle knobBounds = new Rectangle(0, 0, 0,0);
private final Rectangle touchBounds = new Rectangle(0, 0, 0,0);
private final Rectangle deadzoneBounds = new Rectangle(0, 0, 0,0);
private final Vector2 knobPosition = new Vector2();
private final Vector2 knobPercent = new Vector2();
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, Skin skin) {
this(deadzoneWidth, skin.get(HorizontalTouchpadStyle.class));
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, Skin skin, String styleName) {
this(deadzoneWidth, skin.get(styleName, HorizontalTouchpadStyle.class));
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public HorizontalTouchpad (float deadzoneWidth, HorizontalTouchpadStyle style) {
if (deadzoneWidth < 0) throw new IllegalArgumentException("deadzoneWidth must be > 0");
this.deadzoneWidth = deadzoneWidth;
knobPosition.set(getWidth() / 2f, getHeight() / 2f);
setStyle(style);
setSize(getPrefWidth(), getPrefHeight());
addListener(new InputListener() {
#Override
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
if (touched) return false;
touched = true;
calculatePositionAndValue(x, y, false);
return true;
}
#Override
public void touchDragged (InputEvent event, float x, float y, int pointer) {
calculatePositionAndValue(x, y, false);
}
#Override
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
touched = false;
calculatePositionAndValue(x, y, resetOnTouchUp);
}
});
}
void calculatePositionAndValue (float x, float y, boolean isTouchUp) {
float oldPositionX = knobPosition.x;
float oldPositionY = knobPosition.y;
float oldPercentX = knobPercent.x;
float oldPercentY = knobPercent.y;
float centerX = knobBounds.x;
float centerY = knobBounds.y;
knobPosition.set(centerX, centerY);
knobPercent.set(0f, 0f);
if (!isTouchUp) {
if (!deadzoneBounds.contains(x, y)) {
knobPercent.set((x - centerX) / (knobBounds.getWidth()/2), 0);
float length = knobPercent.len();
if (length > 1) knobPercent.scl(1 / length);
if (knobBounds.contains(x, y)) {
knobPosition.set(x, y);
} else {
knobPosition.set(knobPercent).nor().scl(knobBounds.getWidth()/2,0).add(knobBounds.x, knobBounds.y);
}
}
}
if (oldPercentX != knobPercent.x || oldPercentY != knobPercent.y) {
ChangeListener.ChangeEvent changeEvent = Pools.obtain(ChangeListener.ChangeEvent.class);
if (fire(changeEvent)) {
knobPercent.set(oldPercentX, oldPercentY);
knobPosition.set(oldPositionX, oldPositionY);
}
Pools.free(changeEvent);
}
}
public void setStyle (HorizontalTouchpadStyle style) {
if (style == null) throw new IllegalArgumentException("style cannot be null");
this.style = style;
invalidateHierarchy();
}
/** Returns the touchpad's style. Modifying the returned style may not have an effect until {#link #setStyle(HorizontalTouchpadStyle)} is
* called. */
public HorizontalTouchpadStyle getStyle () {
return style;
}
#Override
public Actor hit (float x, float y, boolean touchable) {
if (touchable && this.getTouchable() != Touchable.enabled) return null;
return touchBounds.contains(x, y) ? this : null;
}
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
float radius = Math.min(halfWidth, halfHeight);
touchBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
if (style.knob != null) radius -= Math.max(style.knob.getMinWidth(), style.knob.getMinHeight()) / 2;
knobBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
deadzoneBounds.set(halfWidth, halfHeight, deadzoneWidth, getHeight());
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
#Override
public void draw (Batch batch, float parentAlpha) {
validate();
Color c = getColor();
batch.setColor(c.r, c.g, c.b, c.a * parentAlpha);
float x = getX();
float y = getY();
float w = getWidth();
float h = getHeight();
final Drawable bg = style.background;
if (bg != null) bg.draw(batch, x, y, w, h);
final Drawable knob = style.knob;
if (knob != null) {
x += knobPosition.x - knob.getMinWidth() / 2f;
y += knobPosition.y - knob.getMinHeight() / 2f;
knob.draw(batch, x, y, knob.getMinWidth(), knob.getMinHeight());
}
}
#Override
public float getPrefWidth () {
return style.background != null ? style.background.getMinWidth() : 0;
}
#Override
public float getPrefHeight () {
return style.background != null ? style.background.getMinHeight() : 0;
}
public boolean isTouched () {
return touched;
}
public boolean getResetOnTouchUp () {
return resetOnTouchUp;
}
/** #param reset Whether to reset the knob to the center on touch up. */
public void setResetOnTouchUp (boolean reset) {
this.resetOnTouchUp = reset;
}
/** #param deadzoneWidth The distance in pixels from the center of the touchpad required for the knob to be moved. */
public void setDeadzone (float deadzoneWidth) {
if (deadzoneWidth < 0) throw new IllegalArgumentException("deadzoneWidth must be > 0");
this.deadzoneWidth = deadzoneWidth;
invalidate();
}
/** Returns the x-position of the knob relative to the center of the widget. The positive direction is right. */
public float getKnobX () {
return knobPosition.x;
}
/** Returns the y-position of the knob relative to the center of the widget. The positive direction is up. */
public float getKnobY () {
return knobPosition.y;
}
/** Returns the x-position of the knob as a percentage from the center of the touchpad to the edge of the circular movement
* area. The positive direction is right. */
public float getKnobPercentX () {
return knobPercent.x;
}
/** Returns the y-position of the knob as a percentage from the center of the touchpad to the edge of the circular movement
* area. The positive direction is up. */
public float getKnobPercentY () {
return knobPercent.y;
}
/** The style for a {#link HorizontalTouchpad}.
* #author Josh Street */
public static class HorizontalTouchpadStyle {
/** Stretched in both directions. Optional. */
public Drawable background;
/** Optional. */
public Drawable knob;
public HorizontalTouchpadStyle () {
}
public HorizontalTouchpadStyle (Drawable background, Drawable knob) {
this.background = background;
this.knob = knob;
}
public HorizontalTouchpadStyle (HorizontalTouchpadStyle style) {
this.background = style.background;
this.knob = style.knob;
}
}
}

If you only allow horizontal movement it's only x-position that's interesting right? You can set y-position to always be a constant value. To make it stay within the bounds of the touchpad you need a check to make sure it doesn't move outside. So if you define some constants inside the layout method:
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
float radius = Math.min(halfWidth, halfHeight);
touchBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
if (style.knob != null) radius -= Math.max(style.knob.getMinWidth(), style.knob.getMinHeight()) / 2;
knobBounds.set(halfWidth, halfHeight, getWidth(),getHeight());
deadzoneBounds.set(halfWidth, halfHeight, deadzoneWidth, getHeight());
yPosition = halfHeight;
minX = style.knob.getMinWidth() / 2;
maxX = getWidth() - style.knob.getMinWidth() / 2;
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
and then when you set the position:
.....
if (knobBounds.contains(x, y)) {
if (x < minX) {
x = minX;
}
if (x > maxX) {
x = maxX;
}
knobPosition.set(x, yPosition);
} else {
knobPosition.set(knobPercent).nor().scl(knobBounds.getWidth()/2,0).add(knobBounds.x, knobBounds.y);
}
.....
Something like that (might need tweeking). You probably have to do a similair check for knobPercent.

Had to adjust the bounds and how it calculates the location and percent based on it being a rectangle versus a circle. Here is the updated layout method and calculate method:
#Override
public void layout () {
// Recalc pad and deadzone bounds
float halfWidth = getWidth() / 2;
float halfHeight = getHeight() / 2;
touchBounds.set(getX(), getY(), getWidth(),getHeight());
knobBounds.set(getX(), getY(), getWidth(),getHeight());
deadzoneBounds.set(halfWidth-deadzoneWidth/2, getY(), deadzoneWidth, getHeight());
yPosition = halfHeight;
// Recalc pad values and knob position
knobPosition.set(halfWidth, halfHeight);
knobPercent.set(0, 0);
}
Added a yPosition variable to make it easy to set the y position everytime
void calculatePositionAndValue (float x, float y, boolean isTouchUp) {
float oldPositionX = knobPosition.x;
float oldPositionY = knobPosition.y;
float oldPercentX = knobPercent.x;
float oldPercentY = knobPercent.y;
float centerX = knobBounds.width/2;
float centerY = knobBounds.height/2;
knobPosition.set(centerX, centerY);
knobPercent.set(0f, 0f);
if (!isTouchUp) {
if (!deadzoneBounds.contains(x, y)) {
knobPercent.set((x - centerX) / (knobBounds.getWidth()/2), 0);
float length = knobPercent.len();
if (length > 1) knobPercent.scl(1 / length);
if (knobBounds.contains(x, y)) {
knobPosition.set(x, yPosition);
} else {
knobPosition.set(knobPercent).scl(knobBounds.getWidth()/2,0).add(knobBounds.width/2, knobBounds.height/2);
}
}
}
if (oldPercentX != knobPercent.x || oldPercentY != knobPercent.y) {
ChangeListener.ChangeEvent changeEvent = Pools.obtain(ChangeListener.ChangeEvent.class);
if (fire(changeEvent)) {
knobPercent.set(oldPercentX, oldPercentY);
knobPosition.set(oldPositionX, oldPositionY);
}
Pools.free(changeEvent);
}
}
The big change here was calculating the percent and changing the position when not within the bounds. It was using getX() for the circle which is the center, but for rectangle it is the bottom left. So had to change it to the center.

Related

Libgdx : GestureListener - pan not working properly

I am new to libgdx and trying to move a circle vy draging it. But its not working properly.
class deceleration is as follows -
public class gScreen1 extends ScreenAdapter
implements GestureDetector.GestureListener {
private static final float WORLD_WIDTH = 640;
private static final float WORLD_HEIGHT = 480;
...
#Override
public void show() {
super.show();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(WORLD_WIDTH / 2, WORLD_HEIGHT / 2, 0);
camera.update();
viewport = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT, camera);
shapeRenderer = new ShapeRenderer();
batch = new SpriteBatch();
}
private void drawGrid() {
Gdx.gl.glLineWidth(2);
shapeRenderer.setProjectionMatrix(camera.projection);
shapeRenderer.setTransformMatrix(camera.view);
shapeRenderer.setColor(Color.YELLOW);
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
size = points.length;
for (int i = 0; i < size; i++) {
shapeRenderer.circle(pointsr[i].x , pointsr[i].y ,10);
}
shapeRenderer.end();
}
#Override
public void render(float delta) {
super.render(delta);
clearScreen();
drawGrid();
}
#Override
public boolean pan(float x1, float y1, float deltaX, float deltaY) {
Vector3 tmpCoords = new Vector3(x1,y1, 0);
camera.unproject(tmpCoords);
int x = (int)tmpCoords.x;
int y = (int)tmpCoords.y;
for( i = 0; i < size ; i++) {
int x2 = pointsr[i].x;
int y2 = pointsr[i].y;
if( ((x2 -x)*(x2 -x) + (y2 -y)*(y2 -y) ) < 400 )
break;
}
if( i < size ) {
pointsr[i].x += deltaX;
pointsr[i].y += deltaY;
}
}
Circles are not following movement of finger, some time they move a little bit but in opposite y direction of touch movement.

Dropshadow border around undecorated jframe

I want to add a dropshadow border around an undecorated JFrame. I found this on stackoverflow: Undecorated JFrame shadow
However this is causing issues with a heavyweight component in my frame from this known issue when I'm setting the JFrame backgound color to translucent.
Is there any other way to accomplish this? I am thinking that I would need a special LAF for my JFrames that has no Title area and no window border, but I have no experience writing LAFs.
JxBrowser is the heavyweight component in my example that renders transparent. They have an option to render in lightweight rendering mode but my memory and cpu skyrocket and is unacceptable. There should be an alternative way to draw a shadow border around my undecorated jframe.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import com.teamdev.jxbrowser.chromium.Browser;
import com.teamdev.jxbrowser.chromium.swing.BrowserView;
public class ShadowWindowBrowser {
private static enum Position {
TOP, TOP_LEFT, LEFT, BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT, RIGHT, TOP_RIGHT
};
private static final Map<Integer, Map<Position, BufferedImage>> CACHE = new HashMap<Integer, Map<Position, BufferedImage>>();
public static void main(String[] args) {
new ShadowWindowBrowser();
}
public ShadowWindowBrowser() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new ShadowPane());
Browser browser = new Browser();
BrowserView browserView = new BrowserView(browser);
browser.loadURL("http://google.com");
JPanel panel = new JPanel(new BorderLayout());
panel.add(browserView, BorderLayout.CENTER);
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ShadowPane extends JPanel {
private Border dropShadowBorder = new DropShadowBorder(UIManager.getColor("Control"), 1, 5, .5f, 12, true, true, true, true);
public ShadowPane() {
setLayout(new BorderLayout());
setOpaque(false);
setBackground(Color.BLACK);
setBorder(dropShadowBorder);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
}
class DropShadowBorder implements Border {
private Color lineColor;
private int lineWidth;
private int shadowSize;
private float shadowOpacity;
private int cornerSize;
private boolean showTopShadow;
private boolean showLeftShadow;
private boolean showBottomShadow;
private boolean showRightShadow;
public DropShadowBorder() {
this(UIManager.getColor("Control"), 1, 5);
}
public DropShadowBorder(Color lineColor, int lineWidth, int shadowSize) {
this(lineColor, lineWidth, shadowSize, .5f, 12, false, false, true, true);
}
public DropShadowBorder(Color lineColor, int lineWidth, boolean showLeftShadow) {
this(lineColor, lineWidth, 5, .5f, 12, false, showLeftShadow, true, true);
}
public DropShadowBorder(Color lineColor, int lineWidth, int shadowSize, float shadowOpacity, int cornerSize,
boolean showTopShadow, boolean showLeftShadow, boolean showBottomShadow, boolean showRightShadow) {
this.lineColor = lineColor;
this.lineWidth = lineWidth;
this.shadowSize = shadowSize;
this.shadowOpacity = shadowOpacity;
this.cornerSize = cornerSize;
this.showTopShadow = showTopShadow;
this.showLeftShadow = showLeftShadow;
this.showBottomShadow = showBottomShadow;
this.showRightShadow = showRightShadow;
}
/**
* #inheritDoc
*/
#Override
public void paintBorder(Component c, Graphics graphics, int x, int y, int width, int height) {
/*
* 1) Get images for this border 2) Paint the images for each side of
* the border that should be painted
*/
Map<Position, BufferedImage> images = getImages(null);
// compute the edges of the component -- not including the border
Insets borderInsets = getBorderInsets(c);
int leftEdge = x + borderInsets.left - lineWidth;
int rightEdge = x + width - borderInsets.right;
int topEdge = y + borderInsets.top - lineWidth;
int bottomEdge = y + height - borderInsets.bottom;
Graphics2D g2 = (Graphics2D) graphics;
g2.setColor(lineColor);
// The location and size of the shadows depends on which shadows are
// being
// drawn. For instance, if the left & bottom shadows are being drawn,
// then
// the left shadow extends all the way down to the corner, a corner is
// drawn,
// and then the bottom shadow begins at the corner. If, however, only
// the
// bottom shadow is drawn, then the bottom-left corner is drawn to the
// right of the corner, and the bottom shadow is somewhat shorter than
// before.
Point topLeftShadowPoint = null;
if (showLeftShadow || showTopShadow) {
topLeftShadowPoint = new Point();
if (showLeftShadow && !showTopShadow) {
topLeftShadowPoint.setLocation(x, y + shadowSize);
} else if (showLeftShadow && showTopShadow) {
topLeftShadowPoint.setLocation(x, y);
} else if (!showLeftShadow && showTopShadow) {
topLeftShadowPoint.setLocation(x + shadowSize, y);
}
}
Point bottomLeftShadowPoint = null;
if (showLeftShadow || showBottomShadow) {
bottomLeftShadowPoint = new Point();
if (showLeftShadow && !showBottomShadow) {
bottomLeftShadowPoint.setLocation(x, y + height - shadowSize - shadowSize);
} else if (showLeftShadow && showBottomShadow) {
bottomLeftShadowPoint.setLocation(x, y + height - shadowSize);
} else if (!showLeftShadow && showBottomShadow) {
bottomLeftShadowPoint.setLocation(x + shadowSize, y + height - shadowSize);
}
}
Point bottomRightShadowPoint = null;
if (showRightShadow || showBottomShadow) {
bottomRightShadowPoint = new Point();
if (showRightShadow && !showBottomShadow) {
bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize - shadowSize);
} else if (showRightShadow && showBottomShadow) {
bottomRightShadowPoint.setLocation(x + width - shadowSize, y + height - shadowSize);
} else if (!showRightShadow && showBottomShadow) {
bottomRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y + height - shadowSize);
}
}
Point topRightShadowPoint = null;
if (showRightShadow || showTopShadow) {
topRightShadowPoint = new Point();
if (showRightShadow && !showTopShadow) {
topRightShadowPoint.setLocation(x + width - shadowSize, y + shadowSize);
} else if (showRightShadow && showTopShadow) {
topRightShadowPoint.setLocation(x + width - shadowSize, y);
} else if (!showRightShadow && showTopShadow) {
topRightShadowPoint.setLocation(x + width - shadowSize - shadowSize, y);
}
}
if (showLeftShadow) {
Rectangle leftShadowRect = new Rectangle(x, (int) (topLeftShadowPoint.getY() + shadowSize), shadowSize,
(int) (bottomLeftShadowPoint.getY() - topLeftShadowPoint.getY() - shadowSize));
g2.drawImage(images.get(Position.LEFT).getScaledInstance(leftShadowRect.width, leftShadowRect.height,
Image.SCALE_FAST), leftShadowRect.x, leftShadowRect.y, null);
}
if (showBottomShadow) {
Rectangle bottomShadowRect = new Rectangle((int) (bottomLeftShadowPoint.getX() + shadowSize),
y + height - shadowSize,
(int) (bottomRightShadowPoint.getX() - bottomLeftShadowPoint.getX() - shadowSize), shadowSize);
g2.drawImage(images.get(Position.BOTTOM).getScaledInstance(bottomShadowRect.width, bottomShadowRect.height,
Image.SCALE_FAST), bottomShadowRect.x, bottomShadowRect.y, null);
}
if (showRightShadow) {
Rectangle rightShadowRect = new Rectangle(x + width - shadowSize,
(int) (topRightShadowPoint.getY() + shadowSize), shadowSize,
(int) (bottomRightShadowPoint.getY() - topRightShadowPoint.getY() - shadowSize));
g2.drawImage(images.get(Position.RIGHT).getScaledInstance(rightShadowRect.width, rightShadowRect.height,
Image.SCALE_FAST), rightShadowRect.x, rightShadowRect.y, null);
}
if (showTopShadow) {
Rectangle topShadowRect = new Rectangle((int) topLeftShadowPoint.getX() + shadowSize, y,
(int) (topRightShadowPoint.getX() - topLeftShadowPoint.getX() - shadowSize), shadowSize);
g2.drawImage(images.get(Position.TOP).getScaledInstance(topShadowRect.width, topShadowRect.height,
Image.SCALE_FAST), topShadowRect.x, topShadowRect.y, null);
}
if (showLeftShadow || showTopShadow) {
g2.drawImage(images.get(Position.TOP_LEFT), null, (int) topLeftShadowPoint.getX(),
(int) topLeftShadowPoint.getY());
}
if (showLeftShadow || showBottomShadow) {
g2.drawImage(images.get(Position.BOTTOM_LEFT), null, (int) bottomLeftShadowPoint.getX(),
(int) bottomLeftShadowPoint.getY());
}
if (showRightShadow || showBottomShadow) {
g2.drawImage(images.get(Position.BOTTOM_RIGHT), null, (int) bottomRightShadowPoint.getX(),
(int) bottomRightShadowPoint.getY());
}
if (showRightShadow || showTopShadow) {
g2.drawImage(images.get(Position.TOP_RIGHT), null, (int) topRightShadowPoint.getX(),
(int) topRightShadowPoint.getY());
}
}
private Map<Position, BufferedImage> getImages(Graphics2D g2) {
// first, check to see if an image for this size has already been
// rendered
// if so, use the cache. Else, draw and save
Map<Position, BufferedImage> images = CACHE.get(shadowSize);
if (images == null) {
images = new HashMap<Position, BufferedImage>();
/*
* Do draw a drop shadow, I have to: 1) Create a rounded rectangle
* 2) Create a BufferedImage to draw the rounded rect in 3)
* Translate the graphics for the image, so that the rectangle is
* centered in the drawn space. The border around the rectangle
* needs to be shadowWidth wide, so that there is space for the
* shadow to be drawn. 4) Draw the rounded rect as black, with an
* opacity of 50% 5) Create the BLUR_KERNEL 6) Blur the image 7)
* copy off the corners, sides, etc into images to be used for
* drawing the Border
*/
int rectWidth = cornerSize + 1;
RoundRectangle2D rect = new RoundRectangle2D.Double(0, 0, rectWidth, rectWidth, cornerSize, cornerSize);
int imageWidth = rectWidth + shadowSize * 2;
BufferedImage image = new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB);
Graphics2D buffer = (Graphics2D) image.getGraphics();
buffer.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
buffer.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
buffer.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
buffer.setColor(new Color(0.0f, 0.0f, 0.0f, shadowOpacity));
buffer.translate(shadowSize, shadowSize);
buffer.fill(rect);
float blurry = 1.0f / (shadowSize * shadowSize);
float[] blurKernel = new float[shadowSize * shadowSize];
for (int i = 0; i < blurKernel.length; i++) {
blurKernel[i] = blurry;
}
ConvolveOp blur = new ConvolveOp(new Kernel(shadowSize, shadowSize, blurKernel));
BufferedImage targetImage = new BufferedImage(imageWidth, imageWidth, BufferedImage.TYPE_INT_ARGB);
((Graphics2D) targetImage.getGraphics()).drawImage(image, blur, -(shadowSize / 2), -(shadowSize / 2));
int x = 1;
int y = 1;
int w = shadowSize;
int h = shadowSize;
images.put(Position.TOP_LEFT, targetImage.getSubimage(x, y, w, h));
x = 1;
y = h;
w = shadowSize;
h = 1;
images.put(Position.LEFT, targetImage.getSubimage(x, y, w, h));
x = 1;
y = rectWidth;
w = shadowSize;
h = shadowSize;
images.put(Position.BOTTOM_LEFT, targetImage.getSubimage(x, y, w, h));
x = cornerSize + 1;
y = rectWidth;
w = 1;
h = shadowSize;
images.put(Position.BOTTOM, targetImage.getSubimage(x, y, w, h));
x = rectWidth;
y = x;
w = shadowSize;
h = shadowSize;
images.put(Position.BOTTOM_RIGHT, targetImage.getSubimage(x, y, w, h));
x = rectWidth;
y = cornerSize + 1;
w = shadowSize;
h = 1;
images.put(Position.RIGHT, targetImage.getSubimage(x, y, w, h));
x = rectWidth;
y = 1;
w = shadowSize;
h = shadowSize;
images.put(Position.TOP_RIGHT, targetImage.getSubimage(x, y, w, h));
x = shadowSize;
y = 1;
w = 1;
h = shadowSize;
images.put(Position.TOP, targetImage.getSubimage(x, y, w, h));
buffer.dispose();
image.flush();
}
return images;
}
/**
* #inheritDoc
*/
#Override
public Insets getBorderInsets(Component c) {
int top = showTopShadow ? lineWidth + shadowSize : lineWidth;
int left = showLeftShadow ? lineWidth + shadowSize : lineWidth;
int bottom = showBottomShadow ? lineWidth + shadowSize : lineWidth;
int right = showRightShadow ? lineWidth + shadowSize : lineWidth;
return new Insets(top - 1, left - 1, bottom - 1, right - 1);
}
/**
* #inheritDoc
*/
#Override
public boolean isBorderOpaque() {
return true;
}
public boolean isShowTopShadow() {
return showTopShadow;
}
public boolean isShowLeftShadow() {
return showLeftShadow;
}
public boolean isShowRightShadow() {
return showRightShadow;
}
public boolean isShowBottomShadow() {
return showBottomShadow;
}
public int getLineWidth() {
return lineWidth;
}
public Color getLineColor() {
return lineColor;
}
public int getShadowSize() {
return shadowSize;
}
public float getShadowOpacity() {
return shadowOpacity;
}
public int getCornerSize() {
return cornerSize;
}
}
}

Spawning random different objects at random locations

I am making a game where there are different objects fall from the upper part of the screen to the lower part of the screen. I am having problems with how can I choose what TextureRegion randomly to be spawned and not to change the already spawned TextureRegions. When I run the game lets say that the electronRegion is spawned first then when the next is spawned and let's say it's an antiprotonRegion the first electronRegion changes to antiprotonRegion that I don't want.
Here is my game class:
public class GameScreenTest implements Screen {
...
#Override
public void render(float delta) {
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
game.batch.disableBlending();
game.batch.draw(background, 0, 0);
game.batch.enableBlending();
for(Particles particle: particles) {
particlesControl.draw(particle.x, particle.y);
}
game.batch.end();
if(TimeUtils.millis() - lastDropTime > 500) {
particlesControl.spawn();
particlesControl.update();
}
Iterator<Particles> iter = particles.iterator();
while(iter.hasNext()) {
Particles particle = iter.next();
particle.y -= 200 * Gdx.graphics.getDeltaTime();
if(particle.y + particle.height < 0) {
iter.remove();
}
}
}
...
private class Particles {
private int width;
private int height;
private int x;
private int y;
private Particles() {
}
private void spawn() {
Particles particle = new Particles();
particle.x = MathUtils.random(0, 480 - width);
particle.y = 800;
particle.width = width;
particle.height = height;
particles.add(particle);
lastDropTime = TimeUtils.millis();
}
private void update() {
choice = MathUtils.random(1, 4);
switch(choice) {
case 1:
chosen = new TextureRegion(protonRegion);
width = 75;
height = 75;
break;
case 2:
chosen = new TextureRegion(electronRegion);
width = 75 / 2;
height = 75 / 2;
break;
case 3:
chosen = new TextureRegion(antiprotonRegion);
width = 75;
height = 75;
break;
case 4:
chosen = new TextureRegion(antielectronRegion);
width = 75 / 2;
height = 75 / 2;
break;
}
}
private void draw(int x, int y) {
game.batch.draw(chosen, x, y, width, height);
}
}
I want to know why does all the spawned objects change every time a random choice is taken, and of course, how to solve this problem. Thank You.
Tray this:
public class GameScreenTest implements Screen {
final AntimatterBlast game;
private Texture gameObjects;
private TextureRegion electronRegion;
private TextureRegion antielectronRegion;
private TextureRegion protonRegion;
private TextureRegion antiprotonRegion;
//===========================================//
//remove private TextureRegion chosen;
//===========================================//
private TextureRegion background;
private Music gameMusic;
private OrthographicCamera camera;
private Array<Particles> particles;
private Particles particlesControl;
private long lastDropTime;
private int choice;
public GameScreenTest(final AntimatterBlast game) {
this.game = game;
gameObjects = new Texture(Gdx.files.internal("GameObjects.png"));
electronRegion = new TextureRegion(gameObjects, 105, 103, 50, 50);
antielectronRegion = new TextureRegion(gameObjects, 105, 155, 46, 46);
protonRegion = new TextureRegion(gameObjects, 6, 6, 100, 100);
antiprotonRegion = new TextureRegion(gameObjects, 6, 108, 90, 90);
background = new TextureRegion(gameObjects, 0, 204, 480, 800);
gameMusic = Gdx.audio.newMusic(Gdx.files.internal("DST-ElektroHauz.mp3"));
gameMusic.setLooping(true);
camera = new OrthographicCamera();
camera.setToOrtho(false, 480, 800);
particles = new Array<Particles>();
particlesControl = new Particles();
//===========================================//
//choice = MathUtils.random(1, 4); //remove
//chosen = new TextureRegion(protonRegion); //remove
//===========================================//
}
#Override
public void render(float delta) {
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
game.batch.disableBlending();
game.batch.draw(background, 0, 0);
game.batch.enableBlending();
for(Particles particle: particles) {
//===========================================//
particlesControl.draw(particle.chosen, particle.x, particle.y);
//change
//particlesControl.draw(particle.x, particle.y);
//===========================================//
}
game.batch.end();
if(TimeUtils.millis() - lastDropTime > 500) {
//===========================================//
particlesControl.spawn();
//===========================================//
//===========================================//
//remove particlesControl.update();
//===========================================//
}
Iterator<Particles> iter = particles.iterator();
while(iter.hasNext()) {
Particles particle = iter.next();
particle.y -= 200 * Gdx.graphics.getDeltaTime();
if(particle.y + particle.height < 0) {
iter.remove();
}
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
gameMusic.play();
particlesControl.spawn();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
gameObjects.dispose();
gameMusic.dispose();
}
private class Particles {
//===========================================//
private TextureRegion chosen; //add variable
//===========================================//
private int width;
private int height;
private int x;
private int y;
private Particles() {
}
private void spawn() {
Particles particle = new Particles();
particle.x = MathUtils.random(0, 480 - width);
particle.y = 800;
particle.width = width;
particle.height = height;
//===========================================//
particle.selectTexture();
//===========================================//
lastDropTime = TimeUtils.millis();
//===========================================//
selectTexture(); //add call
//===========================================//
particles.add(particle);
}
//===========================================//
private void selectTexture() { //change name, but is not nesesari
//===========================================//
choice = MathUtils.random(1, 4);
switch(choice) {
case 1:
//===========================================//
//change
//chosen = new TextureRegion(antielectronRegion);
//if you are not going to change or modific TextureRegion
//independet other the textureRegion,
//I think you could use it well.It is just an idea
chosen = protonRegion;
//===========================================//
width = 75;
height = 75;
break;
case 2:
//===========================================//
//change
//chosen = new TextureRegion(antielectronRegion);
//if you are not going to change or modific TextureRegion
//independet other the textureRegion,
//I think you could use it well.It is just an idea
chosen = electronRegion;
//===========================================//
width = 75 / 2;
height = 75 / 2;
break;
case 3:
//===========================================//
//change
//chosen = new TextureRegion(antielectronRegion);
//if you are not going to change or modific TextureRegion
//independet other the textureRegion,
//I think you could use it well.It is just an idea
chosen = antiprotonRegion;
//===========================================//
width = 75;
height = 75;
break;
case 4:
//===========================================//
//change
//chosen = new TextureRegion(antielectronRegion);
//if you are not going to change or modific TextureRegion
//independet other the textureRegion,
//I think you could use it well.It is just an idea
chosen = antielectronRegion;
//===========================================//
width = 75 / 2;
height = 75 / 2;
break;
}
}
//===========================================//
private void draw(TextureRegion chosen, int x, int y) {
game.batch.draw(chosen, x, y, width, height);
//===========================================//
}
}
}
Like I said in the previous comment the texture (chosen) is in the GameScreenTest and you are using it in the inner class so every single instance of the Particles class will share the same texture.
Not error causing but it might help you to organize the code and make it more readable and by all means you are not obligated to make them just take them as suggestions:
Naming conventions
Naming the class Particles make it seem like the class represents more than 1 Particle, but in fact it represents a single Particle maybe name it only Particle.
Separating the game logic
Make the Particle class deal only with its own problems and don't use it to generate other particles and to add it to an outer class.
Using the constructor since it is already there
Give the Particle the texture that it should use and maybe the coordinates.
Drawing "dead" particle
You are drawing first and asking questions later depending on your game it might feel weird since you will always draw a frame while it no longer exists.
Not really sure on this one
But you might be able to reuse the textures regions instead of a creating a new one of the same texture for every single particle

libgdx - group transformation

I'm new in LibGDX.
I created group with actors in show method and add listener (where i calculate rotation matrix) and want to rotate all actors from group with batch.setTransformMatrix(matrixResult); but nothing happens. How can I force to rotate all actors of group?
#Override
public void show() {
camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.position.set(0f, 0f, - 120);
camera.lookAt(0,0,0);
camera.near = 0.1f;
camera.far = 300f;
camera.update();
stage = new Stage();
shader = new ShaderProgram(vertexShader, fragmentShader);
myBatch = new SpriteBatch();
shapeRenderer = new ShapeRenderer();
sphere = BuildSphere(5, 100);
group = new Group(){
#Override
public void draw(Batch batch, float parentAlpha) {
batch.setTransformMatrix(matrixResult);
super.draw(batch, parentAlpha);
}
};
group.setTouchable(Touchable.enabled);
group.addListener(new InputListener(){
#Override
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
Gdx.app.log("MOUSE_X_BEG", Float.toString(x));
Gdx.app.log("MOUSE_Y_BEG", Float.toString(y));
begX = x;
begY = y;
return true;
}
#Override
public void touchDragged(InputEvent event, float x, float y,
int pointer) {
endX = x;
endY = y;
//isRotate = true;
float translationX = endX-begX;
float translationY = begY-endY;
float length = (float)Math.sqrt(Math.pow(translationX,2) + Math.pow(translationY,2));
Vector3 axis = new Vector3(0, 0, 0);
if(length>0){
if(begX-endX == 0){
axis.x = 1;
}else{
float angle = (float) (Math.atan(translationY/translationX) + Math.PI/2.0*(translationX <0 ? -1 : 1));
axis.z = 1;
axis.x = (float) Math.cos(angle);
axis.y = (float) Math.sin(angle);
}
}
quaternion = new Quaternion(axis, 0.02f*length);
Matrix4 rotation = new Matrix4(quaternion);
matrixResult = rotation.mul(matrixRotationOld);
matrixRotationOld = matrixResult;
}
});
//group.setBounds(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
for (CenterCoordinate cenCoordinate : sphere.getPolygonCenterCoordinates()) {
Vector3 vector = cenCoordinate.getCartesianCenter();
Polygon polygon = new Polygon(1, cenCoordinate.getType().getCode(), cenCoordinate.getRadius(), cenCoordinate.getPsi(),
cenCoordinate.getKsi(), vector, cenCoordinate.getRotation());
group.addActor(polygon);
}
stage.addActor(group);
Gdx.input.setInputProcessor(stage);
}
Polygon extends Actor draw method :
#Override
public void draw(Batch batch, float parentAlpha) {
translation.setToTranslation(vertex.x, vertex.y, vertex.z);
matrix.mul(translation);
shader.begin();
shader.setUniformMatrix("u_worldView", matrix);
mesh.render(shader, GL20.GL_LINE_LOOP, 1, mesh.getNumVertices()-1);
shader.end();
matrix.idt();
}
Rotating the batch will not rotate the group. If you want to rotate everything i would suggest to rotate the camera of the Stage and set the Stages SpriteBatchs ProjectionMatrix to cam.combined. But to rotate only the Group use the rotateBy(degrees) or setRotation(degrees) method.
Rotating the SpriteBatch or the camera is actually only a change of the view, but rotating the Actor (in your case the Group) changes the logic of them.
In the draw() method you then will need to take care about this rotation and then you will have the result you want.

Why does safari merge canvas drawings on mouse move?

The video shows the performance problem of safari on canvas drawing.
The drawing takes 100 msec. at average.
When I move cursor point A to B, other browsers draw the canvas at some points between A and B. But on safari, it generally draws on only near A and near B.
If the drawing time is so small, safari behaves like the others.
I think when safari does a drawing job, the mouse events are not triggered to the browser.
Is there a way to fix this problem?
My code:
public void draw() {
long start = System.currentTimeMillis();
canvas.getContext2d().clearRect(Integer.MIN_VALUE / 2,
Integer.MIN_VALUE / 2, Integer.MAX_VALUE, Integer.MAX_VALUE);
canvas.getContext2d().save();
canvas.getContext2d().setFillStyle("red");
for (int i = 0; i < 10000; i++) {
int x = (int)(Math.random() * 1000);
int y = (int)(Math.random() * 600);
canvas.getContext2d().fillRect(x, y, 15, 15);
}
canvas.getContext2d().restore();
logger.finer("Draw Time: " + (System.currentTimeMillis() - start));
}
canvas.addMouseDownHandler(new MouseDownHandler() {
#Override
public void onMouseDown(MouseDownEvent event) {
event.preventDefault();
startX = event.getX();
startY = event.getY();
dragging = true;
}
});
canvas.addMouseMoveHandler(new MouseMoveHandler() {
#Override
public void onMouseMove(MouseMoveEvent event) {
if(dragging)
{
event.preventDefault();
int x = event.getX();
int y = event.getY();
canvas.getContext2d().translate((-startX + x) / startScale,
(-startY + y) / startScale);
draw();
startX = x;
startY = y;
}
}
});
canvas.addMouseUpHandler(new MouseUpHandler() {
#Override
public void onMouseUp(MouseUpEvent event) {
dragging = false;
}
});