Problems with Instantiating Next and Previous Prefab in Array (Unity 3D) - arrays

I am working on a simple virtual tour app where I place a camera inside a sphere, map the sphere with a 360 photo, and click on arrow objects (sprites) to navigate forward and backward. My script below instantiates the next sphere in the array (mapped with the next 360 photo) while destroying the current sphere, simulating forward movement in the application. It seems to work OK.
public void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
if (hit.collider != null)
{
Destroy(currentObject);
currentIndex++;
currentIndex = currentIndex >= Spheres.Length ? 0 : currentIndex;
currentObject = Instantiate(Spheres[currentIndex]);
}
}
}
}
The question I have is how do I reverse the order if the array so I can click the "back" arrow to instantiate the previous prefab in the array.
I thought this was a simple as currentIndex--; but I cannot get it to work. Any help is great appreciated.
pic of virtual tour in editor

Just invert the wrap-around using
currentIndex--;
if(currentIndex < 0) currentIndex = Spheres.Length - 1;
You also want to do the same for the ++ way btw:
currentIndex++;
if(currentIndex > Spheres.Length - 1) currentIndex = 0;
note the - 1! Your index should never reach Spehres.Length since indices are 0-based. So the last element in an array always has index = array.Length - 1.
A slightly easier way for having a wrap-around in positive way would btw be simply
currentIndex = (currentIndex + 1) % Spheres.Length;

Related

how to decide rotation of a marker or pointer on a 3d .obj Object so that marker will look like it is pointing to some area of 3D object

I am looking something like this ( https://jsfiddle.net/xg1o4ekr/378/ ) instead of globe imagine various other shapes of 3D object and marker coming outwards. Since my 3d object is not circle i cannot use the given above code
On clicking a loaded .obj 3D model, there will a pointer rendered on a clicked position(raycaster is used). using three js geometry shapes i have created a pointer. that pointer will render on a clicked position. how to determine pointer’s rotation. (it should dynamically decide the rotation of the pointer)
onMouseClick = (event) => {
event.preventDefault();
// calculate objects intersecting the picking ray
var intersects = this.getIntersects(event.layerX, event.layerY);
if (intersects.length > 0) {
var res = intersects.filter(function (res) {
return res && res.object;
})[0];
if (res && res.object) {
this.selectedObject = res.object;
console.log(res);
const Pointer = this.createPointer();
var g2 = new THREE.Group();
g2.add(Pointer.cone);
g2.add(Pointer.sphere);
g2.position.set(res.point.x, res.point.y, res.point.z);
//g2.rotation.set(res.point.x, res.point.y, res.point.z); //not working fine
g2.rotateX(0.7);
g2.rotateZ(0.9);
g2.rotateY(1);// not working fine
this.scene.add(g2);
}
}
}
getIntersects = (x, y) => {
x = (x / this.width) * 2 - 1;
y = - (y / this.height) * 2 + 1;
this.mouseVector.set(x, y);
this.raycaster.setFromCamera(this.mouseVector, this.camera);
return this.raycaster.intersectObject(this.group, true);
}
Interesting question. It's late here so i wont be able to try code, but maybe this helps?
Does the object you are clicking on have normals?
You can find it in intersets[0].face.normal (or .vertexNormals).
You can align the pointer with that..
var normal = intersects[0].face.normal:
var axis = new THREE.Vector3(0, 1, 0); //(or maybe -1? i'm not sure, play around)
g2.quaternion.setFromUnitVectors(axis, normal);
and if you want to set the pointer at a distance from the surface;
var distance = 4;
g2.position.add( normal.multiplyScalar(distance) );
Also, instead of creating a pointer everytime, why not creating it once and do pointer.clone()?

How to add Movie Clips to Array?

Hey everyone so I have been at this for awhile now and I'm trying to figure out the best way to go about this. So I have an array of Movie Clip Objects calledouterPlanets and they are added to an array called aPlanetArray so these planets in the array are added to the stage and have spacing between them all. When the Player touches the screen the character jumps on another planet and the planets scroll down on the +y axis to keep the character positioned to the center.
I add 10 planets to the stage because for performance Issues I don't want to add a lot and lose FPS so my idea was when the array is getting low such as the planet array is <= 5 then add more planets to the top position of the last last planet. Hope I am making sense. Think of it like a stack of blocks the blocks fall down one by one and more is added to the top of them as more fall down so its never ending.
Here is how I add them to the stage:
//Instantiate Arrays
aPlanetArray = new Array();
//Numbers
xSpacing = 100;
ySpacing = 200;
startPoint = new Point((stage.stageWidth / 2), (stage.stageHeight / 2) );
addOuterPlanets();
private function addOuterPlanets():void
{
for (var i:int = 0; i < nPlanets; i++)
{
outerPlanets = new mcOuterPlanets();
outerPlanets.x = startPoint.x + (xSpacing * i);
outerPlanets.y = startPoint.y - (ySpacing * i);
stage.addChild(outerPlanets);
aPlanetArray.push(outerPlanets);
}
}
The only thing I can come up with at the moment is this:
if (aPlanetArray.length <= 5)
{
addOuterPlanets();
}
This adds a new set of planets but of course just adds them to the center of the stage and not on top of the other planets. Any idea how to accomplish this?
Current Progress:
private function collisionPlanetHandler():void
{
for (var i:int = 0; i < aPlanetArray.length; i++)
{
var currentPlanet:mcOuterPlanets = aPlanetArray[i];
planetContainer.addChild(aPlanetArray[i]);
if (character.hitTestObject(currentPlanet) && !nextlevel)
{
trace("HIT");
yDown = (stage.stageHeight / 2) - (currentPlanet.y - 200); //have object tween to center of stage or where was last positioned
//tap back to false
tap = false;
nextlevel = true;
if (!bNullObject) // have null object so doesnt loop again and cause error for planet == null
{
planet.destroy();
planet = null;
}
bNullObject = true;
planetHit = currentPlanet; // to land on correct planet
aPlanetArray.splice(i, 1);
randomRotation = randomNumber(1, 2); //Stop in random rotation for next planet
TweenLite.to(planetContainer, 2.0, { y:yDown, ease:Elastic.easeOut } );
planetIncrement -= 300;
addPlanet(randomNumber((stage.stageWidth/2) - 220, ((stage.stageWidth/2)) + 220), planetIncrement);
}
}
}
example code
function gameSetup():void{
setupUsers();
loadSounds();
createLevel(_level1);
addInitialPlanets();
addCharacter();
}
private function addInitialPlanets():void{
for (var i:int = 0; i < nPlanets; i++){
addPlanet(startPoint.x + xSpacing * i, startPoint.y + ySpacing * i);
}
}
private function addPlanet(xPos:Number, yPos:Number):void{
p = new mcOuterPlanets();
// var p:mcOuterPlanets = new mcOuterPlanets(); // this is preferred method
p.x = xPos;
p.y = yPos;
stage.addChild(p);
// addChild(p); // this is preferred method
aPlanetArray.push(p);
}
Now when you need to add another planet above the others do
addPlanet(xPos, yPos);
// where xPos is desired X and yPos is desired y
You see what is happening here? I'm positioning the planet by using a separate function. You can offload other tasks related to add a planet as well. Imagine something like this:
addPlanet(xPos, yPos, color, size, speed, ringCount);
Get the idea?
Also you'll want to remove the planets that are far below the player to prevent slowdown. Or you could simply move the ones far below up to the top to recycle them instead of creating new planets all the time.
Draft example...
#Nathan , Neal Davis is right as usual, but I'd push the Vector, then addChild at its current index... Avoid the Arrays if You can. (if the instances are made by the same Class).
var aPlanetArray:<Vector>.MovieClip = new <Vector>.MovieClip(10);
// or new <Vector>.MovieClip(); if You don't want to restrict the amount of items in the Vector.
// or new <Vector>.McOuterPlanets() if McOuterPlanets is a Class.
private function addOuterPlanets():void {
for (var i:int = 0; i < nPlanets; i++){
aPlanetArray.push(new mcOuterPlanets());
aPlanetArray[i].x = startPoint.x + (xSpacing * i);
aPlanetArray[i].y = startPoint.y - (ySpacing * i);
addChild(aPlanetArray[i]);
};
[EDIT]
If you choose to make a Vector of MovieClip (var aPlanetArray:<Vector>.MovieClip = new <Vector>.MovieClip(10)),
You must use aPlanetArray[i] as McOuterPlanets to get the methods of
Your McOuterPlanets methods!
So, in the case of a Vector of MovieClip (aPlanetArray == .MovieClip), if You want to use the methods of McOuterPlanets methods You have to do :
var outerP : McOuterPlanets = aPlanetArray[someIndex] as McOuterPlanets;.
Then You may call outerP.someMethodOfMcOuterPlanets();
Just because McOuterPlanets extends the MovieClip Class.
Never use Vectors if You have to add different types of datas in Your Vector, use the Array Class instead.
This will works but this is tricky an total nonsense!
Example :
var vectorOfStrings:Vector.<String> = Vector.<String>([["a","b","c"],["d","e","f"]]);
// this works and Your Vector contains only Strings so OK.
trace("");
trace("length of the Vector = " + vectorOfStrings.length);
trace("vectorOfStrings.toString() = " + vectorOfStrings.toString());
trace("vectorOfStrings[0] = " + vectorOfStrings[0]);
trace("vectorOfStrings[1] = " + vectorOfStrings[1]);
var vectorOfArrays:Vector.<Array> = Vector.<Array>([["a","b","c"],[1,2,3]]);
// this works but this is tricky an total nonsense
// use the Array Class instead!
trace("\n NEVER DO THIS! Use the Array Class instead!");
trace(" NONSENSE!");
trace("length of the Vector = " + vectorOfArrays.length);
trace("vectorOfArrays.toString() = " + vectorOfArrays.toString());
trace("vectorOfArrays[0] = " + vectorOfArrays[0]);
trace("vectorOfArrays[1] = " + vectorOfArrays[1]);
[/EDIT]
I hope this may help.
Check to the reference for :
ActionScript 3 fundamentals: Arrays
ActionScript 3 fundamentals: Associative arrays, maps, and dictionaries
ActionScript 3 fundamentals: Vectors and ByteArrays

Trouble displaying player Vector lives on stage

Hey everyone so I am having some trouble displaying the player Vector lives in a Array added to the stage. When I first start my game the lives are added correctly and when an enemy hits the player they are removed correctly but when I added a store where you can purchase an extra life I am getting a bug to where the vector lives are added to the stage but they dont remove when an enemy hits the player and they just are there.
in the constructor I have nLives = 2;
addPlayerLivesToStage();
Here is the addPlayerLivesToStage(); function as well as the removePlayerLives();
private function addPlayerLivesToStage():void
{
var startPoint:Point = new Point((stage.stageWidth / 2) + 240, (stage.stageHeight / 2) - 180);
var xSpacing:Number = 30;
lives = new Vector.<Sprite>();
for (var i:int = 0; i < nLives; i++)
{
var playerLives:mcPlayerLives = new mcPlayerLives();
playerLives.x = startPoint.x + (xSpacing * i);
playerLives.y = startPoint.y;
addChild(playerLives);
lives.push(playerLives);
//aPlayerLivesArray.push(playerLives);
}
}
private function removePlayerLive():void
{
//Remove live from screen
removeChild(lives[lives.length - 1]);
lives[lives.length - 1] = null;
lives.splice(lives.length - 1, 1);
}
Now in my games ENTER.FRAME Loop I have the function purchaseExtraLifeFunc(); that controls the Purchase and is supposed to add 1 life to the stage.
Like So:
private function purchaseExtraLifeFunc():void
{
if (nCoins >= 5 && purchaseItem)
{
trace("purchase life");
//remove coins from cost of purchase
nCoins -= 5;
updatecoinsTextScore();
//add extra life
nLives += 1;
addPlayerLivesToStage();
}
else
{
purchaseItem = false;
}
}
as you can say I add 1 Live and call the addPlayerLivesToStage function.
THis is not working correctly and I'm all out of ideas if anyone could help me with this I would really aprreciate it
I can see a few logic problems that could be the cause.
It looks like your addPlayerLivesToStage method ignores the fact that it could have been called before, and there could already be lives Sprites added to the stage. You then overwrite the lives Vector with a new Vector. So you don't have access to the previously added Sprites anymore.
I bet if you made the lives Sprites slightly transparent, you would see them stack over each other as you purchase more lives. Or to test, you could store a number that you increment every time you add a lives Sprite in your loop. And set the Y of the new sprite to this number. That way you would see all the Sprites you are adding.
To fix this, you want to remove all the lives Sprites at the beginning of your addPlayerLivesToStage function, because you are recreating them later in that function anyway.
EDIT*
The problem is still that the Sprites are not being cleaned up between addPlayerLivesToStage calls.
It is easier if you first use a container Spirte to hold all you life sprites. So add a private property to your class:
private var livesContainer:Sprite;
Then in you constructor, just set it up:
livesContainer = new Sprite();
livesContainer.x = (stage.stageWidth / 2) + 240;
livesContainer.y = (stage.stageHeight / 2) - 180;
addChild(livesContainer);
The you can just add all the lives into this container.
I modified your addPlayerLivesToStage function to remove any left over life sprites at the end, and cleaned up the adding, so it does not add a life that is already added.
private function addPlayerLivesToStage():void
{
var xSpacing:Number = 30;
// Add new life sprites if we need to.
for (var i:int = lives.length; i < nLives; i++)
{
var playerLives:mcPlayerLives = new mcPlayerLives();
playerLives.x = xSpacing * i;
livesContainer.addChild(playerLives);
lives.push(playerLives);
}
// Remove any life Sprites that are left over from last time.
for (var j:int = lives.length - 1; j > nLives; j--)
{
var life:Sprite = lives[j]
livesContainer.removeChild(life);
lives.splice(j, 1);
}
}

Is there a more efficient way to detect polygon overlap/intersection than PathGeometry.FillContainsWithDetail()?

I have a method that is gobbling up 25% of my cpu time. I call this method about 27,000 times per second. (Yup, lots of calls since it's updating frequently). I am wondering if anybody knows a faster way to detect if 2 polygons overlap. Basically, I have to check the moving objects on the screen against stationary objects on the screen. I am using PathGeometry and the two calls below are using up 25% of the cpu time used by my program. The PointCollection objects I am passing just contain 4 points representing 4 corners of a polygon. They may not create a rectangular area, but all the points are connected. I guess a trapazoid would be the shape.
These methods are short and were very easy to implement, but I think I might want to opt for a more complicated solution if I can have it run more quickly than the code below. Any ideas?
public static bool PointCollectionsOverlap(PointCollection area1, PointCollection area2)
{
PathGeometry pathGeometry1 = GetPathGeometry(area1);
PathGeometry pathGeometry2 = GetPathGeometry(area2);
return pathGeometry1.FillContainsWithDetail(pathGeometry2) != IntersectionDetail.Empty;
}
public static PathGeometry GetPathGeometry(PointCollection polygonCorners)
{
List<PathSegment> pathSegments = new List<PathSegment>
{ new PolyLineSegment(polygonCorners, true) };
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(new PathFigure(polygonCorners[0], pathSegments, true));
return pathGeometry;
}
Ok, after lots of research and finding many partial answers, but none that fully answered the question, I have found a faster way and it is actually about 4.6 times faster than the old way.
I created a special test app to test the speed this. You can find the test app here. If you download it, you can see a checkbox at the top of the app. Check and uncheck it to switch back and forth between the old way and the new way. The app generates a bunch of random polygons and the borders of the polygons change to white when they intersect another polygon. The numbers to the left of the 'Redraw' button are to allow you to enter the Number of Polygons, Max Length of a side, and Max offset from square (to make them less square and more odd shaped). Push 'Refresh' to clear and regenerate new polygons with the settings you've entered.
Anyway, here is the code for the two different implementations. You pass in a collection of the points that make up each polygon. The old way uses less code, but is 4.6 times slower than the new way.
Oh, one quick note. The new way has a couple calls to 'PointIsInsidePolygon'. These were necessary because without it, the method returned false when one polygon was entirely contained within a different polygon. But the PointIsInsidePolygon method fixes that problem.
Hope this all helps somebody else out with polygon intercepts and overlaps.
Old Way (4.6 times slower. YES REALLY 4.6 TIMES slower):
public static bool PointCollectionsOverlap_Slow(PointCollection area1, PointCollection area2)
{
PathGeometry pathGeometry1 = GetPathGeometry(area1);
PathGeometry pathGeometry2 = GetPathGeometry(area2);
bool result = pathGeometry1.FillContainsWithDetail(pathGeometry2) != IntersectionDetail.Empty;
return result;
}
public static PathGeometry GetPathGeometry(PointCollection polygonCorners)
{
List<PathSegment> pathSegments = new List<PathSegment> { new PolyLineSegment(polygonCorners, true) };
PathGeometry pathGeometry = new PathGeometry();
pathGeometry.Figures.Add(new PathFigure(polygonCorners[0], pathSegments, true));
return pathGeometry;
}
New Way (4.6 times faster. YES REALLY 4.6 TIMES faster):
public static bool PointCollectionsOverlap_Fast(PointCollection area1, PointCollection area2)
{
for (int i = 0; i < area1.Count; i++)
{
for (int j = 0; j < area2.Count; j++)
{
if (lineSegmentsIntersect(area1[i], area1[(i + 1) % area1.Count], area2[j], area2[(j + 1) % area2.Count]))
{
return true;
}
}
}
if (PointCollectionContainsPoint(area1, area2[0]) ||
PointCollectionContainsPoint(area2, area1[0]))
{
return true;
}
return false;
}
public static bool PointCollectionContainsPoint(PointCollection area, Point point)
{
Point start = new Point(-100, -100);
int intersections = 0;
for (int i = 0; i < area.Count; i++)
{
if (lineSegmentsIntersect(area[i], area[(i + 1) % area.Count], start, point))
{
intersections++;
}
}
return (intersections % 2) == 1;
}
private static double determinant(Vector vector1, Vector vector2)
{
return vector1.X * vector2.Y - vector1.Y * vector2.X;
}
private static bool lineSegmentsIntersect(Point _segment1_Start, Point _segment1_End, Point _segment2_Start, Point _segment2_End)
{
double det = determinant(_segment1_End - _segment1_Start, _segment2_Start - _segment2_End);
double t = determinant(_segment2_Start - _segment1_Start, _segment2_Start - _segment2_End) / det;
double u = determinant(_segment1_End - _segment1_Start, _segment2_Start - _segment1_Start) / det;
return (t >= 0) && (u >= 0) && (t <= 1) && (u <= 1);
}

Connecting two WPF canvas elements by a line, without using anchors?

I have a canvas for diagramming, and want to join nodes in the diagram by directed lines (arrow ends).
I tried the anchor approach, where lines only attach at specific points on the nodes but that did not work for me, it looked like crap.
I simply want a line from the centre of each object to the other, and stop the line at the nodes' edge in order for the arrow end to show properly. But finding the edge of a canvas element to test intersections against has proven difficult.
Any ideas?
I got a method working using the bounding box of the element. It is not perfect, since my elements are not perfectly rectangular, but it looks OK.
Basically I find the bounding box of the element in Canvas coordinates by:
private static Rect GetBounds(FrameworkElement element, UIElement visual)
{
return new Rect(
element.TranslatePoint(new Point(0, 0), visual),
element.TranslatePoint(new Point(element.ActualWidth, element.ActualHeight), visual));
}
Then I find the intersection of the centre-to-centre line against each of the four sides of the bounding box, and use that intersection point to connect the two elements by a Line shape.
I found the intersection code at Third Party Ninjas:
http://thirdpartyninjas.com/blog/2008/10/07/line-segment-intersection/
private void ProcessIntersection()
{
float ua = (point4.X - point3.X) * (point1.Y - point3.Y) - (point4.Y - point3.Y) * (point1.X - point3.X);
float ub = (point2.X - point1.X) * (point1.Y - point3.Y) - (point2.Y - point1.Y) * (point1.X - point3.X);
float denominator = (point4.Y - point3.Y) * (point2.X - point1.X) - (point4.X - point3.X) * (point2.Y - point1.Y);
intersection = coincident = false;
if (Math.Abs(denominator) <= 0.00001f)
{
if (Math.Abs(ua) <= 0.00001f && Math.Abs(ub) <= 0.00001f)
{
intersection = coincident = true;
intersectionPoint = (point1 + point2) / 2;
}
}
else
{
ua /= denominator;
ub /= denominator;
if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
{
intersection = true;
intersectionPoint.X = point1.X + ua * (point2.X - point1.X);
intersectionPoint.Y = point1.Y + ua * (point2.Y - point1.Y);
}
}
}
And voilá! The lines are now drawn as if they go from the centre of each node to the other, but stops approximately at the node's edge so the arrow end is visible.
An improvement of this method would be to test against the actual edge of the node itself, such as for elliptical nodes, but I have yet to find a WPF method that provides me with a Geometry or Path that I can test against.

Resources