Leaves One

Alan Richard's Blog

Cursor operations on MapView - EusMapDisplay Plugin

About two weeks ago, one of my collaborators told me that he was setting up a virtual university in Minecraft, and was trying to utilize existing image on map plugins to display slides in his server. I thought it must be exhausting to play slides in this way, so I decided to develop a plugin to ease his pain.

EusMapDisplay

First things first, a mechanism for detecting where the user is targeting at the hung map is required for achieve the goal.

And here’s the code:

Location getTargetPoint(Location eyeLocation, Entity entity) {
Vector vector = eyeLocation.getDirection();
vector.normalize().multiply(entity.getLocation().distance(eyeLocation));
eyeLocation.add(vector);
return eyeLocation;
}

This is not enough. With the method above, they only thing we get to know is the exact coordinates where the player is looking at, but what we actually want is the 2D coordinates inside a display.

This gif shows the first version of the 2D coordinates conversion, it’s bogus.

First version

The cursor just won’t move to where we want it to be.

So here’s what I eventually figured out about making the cursor being accurate:

int[] getTargetAbsoluteCoordinates(int windowX, int windowY, Entity frame, Location playerLocation) {
Location eyeLocation = new Location(playerLocation.getWorld(), playerLocation.getX(), playerLocation.getY(), playerLocation.getZ());
eyeLocation.setY(eyeLocation.getY() + 2);
eyeLocation.setDirection(playerLocation.getDirection());

Location targetLocation = getTargetPoint(eyeLocation, frame); // the coordinates where the player is looking at
Location frameLocation = frame.getLocation();

boolean facingXAxis = frame.getFacing() == BlockFace.EAST || frame.getFacing() == BlockFace.WEST;
double targetCenterX = facingXAxis ? targetLocation.getZ() : targetLocation.getX();

double frameCenterX = facingXAxis ? frameLocation.getZ() : frameLocation.getX();

boolean flip = frame.getFacing() == BlockFace.EAST || frame.getFacing() == BlockFace.NORTH;

int relativeX = (int) ((targetCenterX - frameCenterX + (frame.getFacing() == BlockFace.EAST || frame.getFacing() == BlockFace.NORTH ? -0.5 : 0.5)) * 128) * (flip ? -1 : 1);

int relativeY = 180 - (int)((targetLocation.getY() - frame.getLocation().getY() + 0.5) * 128);

return Display.getAbsoluteCoordinate(windowX, windowY, relativeX, relativeY);
}

The getTargetAbsoluteCoordinates() is not elegant so far, but it works fine and satisifys requirements for testing out my idea.

So far so good. Here’s the demo.

Accurate pointing

I validated the algorithem’s correctiveness by putting up the four-directionary “screen cubes” in all of the four quadrants, i.e. +X and +Z, +X and -Z, -X and +Z, -X and -Z.

With the cursor being precise, I added events support.

Events

EusMapDisplay is opensource at https://github.com/richardhyy/EusMapDisplay .

Demo Application: Screen Mirroring

With the framework finished, I made a demo plugin for test purpose.

This plugin mirrors the server’s screen into Minecraft.

Screen Mirroring

Next Step: EusReader

I’ve been working on the slideshow plugin for two days. Till now, it has the ability to manage multiple documents for every single player, and page flipping has been implemented.

While I was developing the plugin, an issue related to wheel scrolling was solved. The problem existed because the listener failed to handling the case where the player swap between the first and the last slot.

First and Last Slot

The following is the polished gist of sourcecode that calculates the wheelAmt value.

MapDisplay mapDisplay = plugin.getMapManager().getMapDisplay(targetDisplay.uuid);
int delta = e.getNewSlot() - e.getPreviousSlot();
if (Math.abs(delta) > 2) {
Integer last = playerDeltaMap.get(e.getPlayer().getUniqueId());
if (last != null) {
delta = (Math.abs(delta) - 7) * (last > 0 ? 1 : -1);
}
}
playerDeltaMap.put(e.getPlayer().getUniqueId(), delta);
mapDisplay.triggerEvent(DisplayEventType.WHEEL_SCROLL, e.getPlayer(), delta, null);

Here’s the scrolling demo:

Scrolling

As you can see, it’s not fast and smooth, so there is still a long way to go before the EusReader plugin become usable.

Pause

The summer vacation is halfway through now. For my further development, it would be better to take a respite and go back to the fundamental knowledge learning.