I've tried on this and this, but I found they only work for mouse events. I would like to drop some pictures on the canvas and let the user to
"touch" on it to drag and drop. But it seems that the picture doesn't receive user's touch event, only the canvas receives the event.
Any suggestion or plugin?
ps: I develop the application on Phonegap and Android system.
you also need to take care of MSPointer events, that are events from Microsoft to manage touch (it was introduced with Win8 and WinPhone 8).
The steps needed, each frame:
detect mouse, touch and MSPointer events
check the cursor position colliding the images
move the image
For the first point:
function getCursorPositions (event, canvas) {
var element = canvas, offsetX = 0, offsetY = 0, positions = [];
if (element.offsetParent) {
do {
offsetX += element.offsetLeft;
offsetY += element.offsetTop;
} while ((element = element.offsetParent));
}
// Add padding and border style widths to offset
/*offsetX += stylePaddingLeft;
offsetY += stylePaddingTop;
offsetX += styleBorderLeft;
offsetY += styleBorderTop;*/
var touch = event;
//if multi-touch, get all the positions
if (event.targetTouches) { // or changedTouches
var touchPoints = (typeof event.targetTouches !== 'undefined') ? event.targetTouches : [event];
for (var i = 0; i < touchPoints.length; i++) {
touch = touchPoints[i];
positions.push({touch.pageX - offsetX, touch.pageY - offsetY});
}
}
else {
positions.push({touch.pageX - offsetX}, {touch.pageY - offsetY});
}
//return positions for mouse or fingers
return positions;
}
For the second point, you have at least 2 ways to detect the collision:
you can check whether the mouse position is inside the bounding box of your item :
function pointIsInRegion (point, targetRegion, threshold) {
return point.x >= (targetRegion.position.x - threshold) &&
point.y >= (targetRegion.position.y - threshold) &&
point.x <= (targetRegion.position.x + targetRegion.dimension.width + threshold) &&
point.y <= (targetRegion.position.y + targetRegion.dimension.height + threshold);
}
Or you can be more accurate by checking the pixels collision.
To achieve this second method, you have to render your items in a temporary canvas and check if there is at least pixels from your 2 items that collide (it can be accelerated by using masks).
For the third point (move the image), all you have to do is to move your image from "currentCursorPosition - previousCursorPosition". That's ths easiest part.
Anyway, I suggest you to use a framework. The code is already done and It will help you to go faster.
cgSceneGraph, (I'm the designer of this framework) will do the Job for you in just few lines.
Have a look at the "planner 2D" and "collision" examples (http://gwennaelbuchet.github.com/cgSceneGraph/examples.html)
Hope this can help you.
Related
I'm hoping to configure the zoom behavior of a plot to have three kinds of interaction:
It should be possible to pan from left to right with the scroll wheel.
It should be possible to pan from left to right with mousedown drag events.
it should be possible to zoom in 1-D with the pinch event (i.e. scroll wheel with control key pressed).
Right now, I can get the latter two to work in this Fiddle: https://jsfiddle.net/s1t7mrpw/6/
The zoomed function looks like this:
function zoomed() {
if (d3.event.sourceEvent.ctrlKey || d3.event.sourceEvent.type === 'mousemove') {
view.attr("transform", d3.event.transform);
centerline.call(xAxis.scale(d3.event.transform.rescaleX(x)));
d3.event.transform.y = 0;
g.attr("transform", d3.event.transform);
} else {
current_transform = d3.zoomTransform(view);
current_transform.x = current_transform.x - d3.event.sourceEvent.deltaY;
// what do I do here to pan the axis and update `view`'s transform?
// centerline.call(xAxis.scale(d3.event.transform.rescaleX(x))); ?
// view.attr('transform', current_transform); ?
g.attr('transform', current_transform);
}
}
It uses the rescale nomenclature from these blocks:
https://bl.ocks.org/mbostock/db6b4335bf1662b413e7968910104f0f
https://bl.ocks.org/rutgerhofste/5bd5b06f7817f0ff3ba1daa64dee629d
And it uses d3-xyzoom for independent scaling in the x and y directions.
https://bl.ocks.org/etiennecrb/863a08b5be3eafe7f1d61c85d724e6c4
But I can't figure out how to pan the axis in the else bracket, when the user is just scrolling (without pressing the control key).
I had previously used a separate trigger for wheel.zoom but then I need to handle the control key in that other function as well.
I basically want the default zoom behaviors for mousemove and when the control key is pressed, but panning rather than zooming on scroll when the control key is not pressed.
I am using d3-zoom on this answer as it is more common and part of the d3 bundle. d3-xyzoom, as an add-on module, adds some useful functionality using d3-zoom as a base, so this solution should work with xyzoom with minor revisions - but I don't believe its use is necessary
The key challenge lies in using the wheel to apply a translate change not a scale change. Roughly speaking, when a scroll event without control occurs you grab the transform on the selection (this should be the node) and update it by taking the scroll and creating an x offset from it to translate the view rectangle. But, you don't update the zoomTransform to nullify the change in scale caused by the zoom - so when you pan by dragging, the rectangle changes size because you haven't updated the zoomTransform used by the zoom behavior. Likewise, the translate can be off if it doesn't account for scale.
The disassociation between zoom transform applied to an element as a "transform" attribute and the zoom transform tracked by the zoom behavior is the cause of many headaches on SO - but it allows freer implementation options as one not need use an element's transform attribute to apply a zoom behavior (useful in canvas, zooming unorthodox things like color, etc).
I'm going to propose a basic solution here.
First, the zoom transform tracked by the zoom behavior can be updated by modifying the d3.event.transform object itself (or with d3.zoomTransform(selection.node) where selection is the selection that the zoom was called on originally).
Secondly, as the d3.event.transform object registers scrolling as scaling, we need to override this and convert this scaling into translating - when needed. To do so, we need to keep track of the current (pre-event) translate and scale - so we can modify them manually:
// Keep track of zoom state:
var k = 1;
var tx = 0;
function zoomed() {
var t = d3.event.transform;
// If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
t.k > k ? tx += 40/k : tx -= 40/k;
t.k = k;
t.x = tx;
}
// otherwise, proceed as normal, but track current k and tx:
else {
k = t.k;
tx = t.x;
}
// Apply the transform:
view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
axisG.call(xAxis.scale(t.rescaleX(xScale)));
}
Above, I modify the transform t.k and t.k for events where wheeling/scrolling should translate. By comparing t.k with the stored k value I can determine direction: would it normally be a zoom out or zoom in event, and then I can convert this into a change in the current translate value. By setting t.k back to the original k value, I can stop the change in scaling that would otherwise occur.
For other zoom events it is business as usual, except, I store the t.x and t.k values for later, for when wheel events should translate rather than scale.
Note: I've set the y scaling and translate in the transform to a hard coded zero, so we don't need to worry about the y component at all.
var width = 500;
var height = 120;
// Keep track of zoom state:
var k = 1;
var tx = 0;
// Some text to show the transform parameters:
var p = d3.select("body")
.append("p");
// Nothing unordinary here:
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var xScale = d3.scaleLinear()
.domain([0,100])
.range([20,width-20])
var xAxis = d3.axisTop(xScale);
var axisG = svg.append("g")
.attr("transform","translate(0,50)")
.call(xAxis);
var view = svg.append("rect")
.attr("width", 20)
.attr("height",20)
.attr("x", width/2)
.attr("y", 60)
var zoom = d3.zoom()
.on("zoom",zoomed);
svg.call(zoom)
function zoomed() {
var t = d3.event.transform;
// If control is not pressed and a wheel was turned, set the scale to last known scale, and modify transform.x by some amount:
if(!d3.event.sourceEvent.ctrlKey && d3.event.sourceEvent.type == "wheel") {
t.k > k ? tx += 40/k : tx -= 40/k;
t.k = k;
t.x = tx;
}
// otherwise, proceed as normal, but track current k and tx:
else {
k = t.k;
tx = t.x;
}
// Apply the transform:
view.attr("transform", "translate("+[t.x,0]+")scale("+[t.k,1]+")");
axisG.call(xAxis.scale(t.rescaleX(xScale)));
// Show current zoom transform:
p.text("transform.k:" + t.k + ", transform.x: " + t.x);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.6.0/d3.min.js"></script>
for my internship in need to make an aplication with three.js what needs to be in a container on a page but it needs an onclick function on the objects. the problem is i cannot find annything on raycasting only in a container and clicking now will not get objects i need
application
onMouseDown(event) {
let s = this;
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
s.mouse.x = ( event.clientX / s.renderer.domElement.clientWidth ) * 2 - 1;
s.mouse.y = - ( event.clientY / s.renderer.domElement.clientHeight ) * 2 + 1;
s.intersects = s.raycaster.intersectObjects(s.blocks, true);
for ( var i = 0; i < s.intersects.length;){
s.intersects[ i ].object.material.color.set( 0xff0000 );
console.log(i)
console.log(s.getScene().children)
console.log(s.intersects)
console.log("test 123")
}
if (s.intersects == 0){
console.log(s.mouse.x)
console.log(s.mouse.y)
}
}
edit: it is not the same as Detect clicked object in THREE.js he is not working in a container. plus he has a little problem with the margins for me everywhere i click on the screen it does not detect what i need and i need it to be working only on the container not the whole webpage plus there help is outdated and is not working annymore
If you are working with a canvas that is not at the top-left corner of the page, you need one more step to get to the normalized device coordinates. Note that the NDC in WebGL are relative to the canvas drawing-area, not the screen or document ([-1,-1] and [1,1] are the bottom-left and top-right corners of the canvas).
Ideally, you'd use ev.offsetX/ev.offsetY, but browser-support for that isn't there yet. Instead, you can do it like this:
const {top, left, width, height} = renderer.domElement.getBoundingClientRect();
mouse.x = -1 + 2 * (ev.clientX - left) / width;
mouse.y = 1 - 2 * (ev.clientY - top) / height;
See here for a working example: https://codepen.io/usefulthink/pen/PVjeJr
Another option is to statically compute the offset-position and size of the canvas on the page and compute the final values based on ev.pageX/ev.pageY. This has the benefit of being a bit more stable (as it is not scrolling-dependent) and would allow to cache the top/left/width/height values.
Good day to all, I am trying to get an enemy boss ship to appear on screen and attack my ship and also dodge my attacks sometimes. So far I have gotten him to attack my ship but hes stays on the edge of the screen. Here is my code :
#pragma strict
// here are public variables for the enemy ship that can be accessed in the inspector
var health:int = 2;
var explosion:GameObject;
var expOrb:GameObject;
var enemyBullet:GameObject;
var expDrop:int = 3;
var hitSound:AudioClip;
var fireRate:float = 2.0;
//heres the private variable counter to keep track of time for fire rate.
private var counter:float = 0.0;
function Update () {
//here we make counter count based on time for the fire rate
counter += Time.deltaTime;
//if the ship goes too far left, we destroy it.
if(transform.position.x < -12){
Destroy(gameObject);
}
//here we shoot 4 bullets if the counter counts higher than the fire rate.
if(counter > fireRate){
var custom1 = Instantiate(enemyBullet, transform.position - Vector3(0.5,0.1,0), Quaternion.Euler(-90,0,0));
var custom2 = Instantiate(enemyBullet, transform.position - Vector3(0.5,0.1,0), Quaternion.Euler(-90,0,0));
var custom3 = Instantiate(enemyBullet, transform.position- Vector3(0.5,0.1,0), Quaternion.Euler(-90,0,0));
var custom4 = Instantiate(enemyBullet, transform.position- Vector3(0.5,0.1,0), Quaternion.Euler(-90,0,0));
//to make the bullets spread, we add extra z velocity to each one to they all move on their own path.
custom1.rigidbody.velocity.z = 3;
custom2.rigidbody.velocity.z = 1;
custom3.rigidbody.velocity.z = -1;
custom4.rigidbody.velocity.z = -3;
counter = 0.0;
}
//end of function update
}
//if a bullet hits the ship, the bullets sends us the hit message to trigger this function to bring down the ships health
function hit () {
health -= 1;
if(health != 0){
if(audio.enabled == true){
audio.PlayOneShot(hitSound);
}
}
if(health <= 0){
onDeath();
}
}
//if health is 0, then this function is triggered to spawn some orbs, spawn the explosion animation object, and destroy itself
function onDeath () {
Instantiate(expOrb,transform.position,Quaternion.Euler(-90,0,0));
expDrop -= 1;
if(expDrop <= 0){
Instantiate(explosion,transform.position,Quaternion.Euler(-90,Random.Range(-180,180),0));
Destroy(gameObject);
}
if(expDrop > 0){
onDeath();
}
}
How do i add the movement aspect to it?
There are many ways to move an object, you can try:
Transform.translate (http://docs.unity3d.com/ScriptReference/Transform.Translate.html)
Modifying transform.position (http://answers.unity3d.com/questions/188998/transformposition.html)
Adding force to it (http://docs.unity3d.com/ScriptReference/Rigidbody.AddForce.html)
And many other ways that I may have not known yet.
For a boss in this kind of game, the movement you may want is either random movement or following the player. Both are not hard to achieve.
Random movement: just do a Random.Range(-1, 1) * bossSpeed *
time.deltaTime and apply it to the boss' x position.
Following the player: get the player's x position then make the boss adjust to the position.
Then how to make the boss dodge the player's bullet sometimes? You can make the boss detect if a player's bullet is incoming by adding a collider thats stands in front of it. If a player's bullet collide with it, then random another number (eg. from 0 to 1). Afterwards, you just need to move the boss away if the random number is 1 and don't move the boss when the random number is 0.
G'day All,
My little game has 5 bouncing balls and 1 player. Initially I wrote the code for the bouncing balls first and each ball has a collision detection method:
foreach (Bouncer bouncer in Game.Components) //For each bouncer component in the game...
{
if (bouncer != this)// Don't collide with myself
{
if (bouncer.collisionRectangle.Intersects(this.collisionRectangle))
{
// How far apart of the positions of the top right hand corners of the sprites when they hit?
int deltaX = Math.Abs((int)this.position.X - (int)bouncer.position.X);
int deltaY = Math.Abs((int)this.position.Y - (int)bouncer.position.Y);
// This is the width and height of a sprite so when two sprites touch this is how far the corners are from each other.
int targetWidth = 80;
int targetHeight = 80;
// The following determins the type of collision (vert hit vs horiz hit)
// Because the app is driven by a game based timer the actual amount of sprite overlap when the collision detection occurs is variable.
// This bit of simple logic has a 10 pixel tollerance for a hit.
// If target - delta is > 10 it will be interpreted as overlap in the non-colliding axis.
// If both if statements are triggered it is interpreted as a corner collision resulting in both sprites rebounding back along the original paths.
if (targetWidth - deltaX < 10) // The hit is a side on hit.
{
this.velocity.X *= -1;
}
if (targetHeight - deltaY < 10) // The hit is a vertical hit
{
this.velocity.Y *= -1;
}
this.numberOfCollisions = this.numberOfCollisions + 1;
}
}
}
base.Update(gameTime);
}
Then I added my player component and the wheels fell off. The app compiles OK but when I run it I get an InvalidCastException and the message:
Unable to cast object of type 'Bounce2.Player' to type 'Bounce2.Bouncer'.
I don't want to include the player object in this collision detector.
Is there a way I can enumerate my way through the Bouncer objects and exclude any other objects?
Thanks,
Andrew.
You can use this:
foreach (Bouncer bouncer in Game.Components.OfType<Bouncer>())
Note that you can store the Bouncer instances in other list too.
I am working on a GTK+ application that uses goocanvas to display a graph on screen. I am having problems coming up with a good way to implement drag scrolling.
Currently the app saves the coordinates where the user clicked and then in a "motion-notify" signal callback, does goo_canvas_scroll_to() to the new position. The problem is that drawing is somewhat slow, and with each pixel moved by the mouse, I get the callback invoked once. This makes the drawing lag behind when dragging the graph around.
Is there a good way to do drag scrolling, so it'd appear more smooth and I could skip some of the redraws?
I was able to get something like this working once by starting a 5ms timer when the user presses the mouse button. In the timer I check where the mouse is and decide which way to scroll, including faster scrolling the closer you are to the edge. The result was very smooth scrolling, at least that's what I remember. Here the guts of it, its gtkmm/c++, but you should be able to get the gist of it:
static const int HOT_AREA = 24;
// convert distance into scroll size. The larger the
// value, the faster the scrolling.
static int accel_fn(int dist) {
if (dist > HOT_AREA)
dist = HOT_AREA;
int dif = dist / (HOT_AREA/4);
if (dif <= 0) dif = 1;
return dif;
}
bool scrollerAddin::on_timeout() {
int ptr_x, ptr_y;
o_scroller->get_pointer(ptr_x, ptr_y);
int vp_width = o_scroller->get_width();
int vp_height = o_scroller->get_height();
if (o_scroller->get_hscrollbar_visible())
vp_height -= o_scroller->get_hscrollbar()->get_height();
if (o_scroller->get_vscrollbar_visible())
vp_width -= o_scroller->get_vscrollbar()->get_width();
if (ptr_x < HOT_AREA)
scroll_left(accel_fn(HOT_AREA-ptr_x));
else if (ptr_x > vp_width - HOT_AREA)
scroll_right(accel_fn(ptr_x - (vp_width - HOT_AREA)));
if (ptr_y < HOT_AREA)
scroll_up(accel_fn(HOT_AREA - ptr_y));
else if (ptr_y > vp_height - HOT_AREA)
scroll_down(accel_fn(ptr_y - (vp_height - HOT_AREA)));
return true;
}
The scroll functions merely adjust the appropriate Adjustment object by the argument.