1

I'm writing a simple 2D game engine because I want to brush up my school math knowledge. I know that maybe a lot of people already answered similar questions (I read a lot of questions and answers about this topic), but I can't manage why my method doesn't work. In this example we can see a "room" with a camera and two rays that change direction depending on the direction of the camera. These rays should represent left and right border of the field of view of the camera and should intersect the walls in front of the camera. The problem is that when the camera moves and rotates (using "UP","DOWN","LEFT","RIGHT"), sometimes the rays disappear i.e. the intersection function fails. Can someone help me find the solution? Here there are the relevant parts of code:

    function Player(x,y,angle) {

        this.x = x;
        this.y = y;
        this.z = 30;
        this.fieldOfView = { 
            degrees : 60,
            rads : Game.utils.degree2Rad(60),
            set : function (deg) {
                var self = this;
                self.fieldOfView.degrees = deg;
                self.fieldOfView.rads = Game.utils.degree2Rad(rads);
            }
        };


        this.angleDeg = angle;
        this.angleRads = Game.utils.degree2Rad(angle);
        this.rotate = function () {
            /* Handle rotation and position
               of the camera depending on the button pressed */
        }
        this.draw = function (canvas1,canvas2) {
            var self = this;
            var ctx1 = canvas1.getContext('2d');

            var ctx2 = canvas2.getContext('2d');

            /*Draw a circle on canvas1 at Game.player.x Game.player.y*/
             // CODE
            /*Draw a circle on canvas2 at the center of canvas*/
             // CODE

            /*Angles of the two rays in radians (FOV is 60°).  
              Add 30° to player's angle and find one,
              Substract 30° to player's angle and find the other
            */
            var rad1 = Game.utils.degree2Rad(Game.player.angleDeg+30);
            var rad2 = Game.utils.degree2Rad(Game.player.angleDeg-30);

            /*
               The two Rays, defined with a point (player.x and player.y)                                          
               and a vector
            */
            var _rad1 = new Ray2D(self.x,self.y,new Vector2D(Math.cos(rad1),Math.sin(rad1)));
            var _rad2 = new Ray2D(self.x,self.y,new Vector2D(Math.cos(rad2),Math.sin(rad2)));

            var _w = Game.walls;
            var ps = [];
            for(var i=0;i<_w.length;i++)
            {
                //FIND THE INTERSECTION POINTS
                var j = _w[i];
                var p =_rad1.intersectionWall(j);
                if(p) {
                    ps.push(p);
                }
                var p2 = _rad2.intersectionWall(j);
                if(p2) {
                    ps.push(p2);
                }

            }

            if(ps.length>1)
            {
                // DRAW THE TWO INTERSECTION POINTS
                ctx1.beginPath();
                ctx1.moveTo(self.x,self.y);
                ctx1.lineTo(ps[0].x,ps[0].y);
                ctx1.stroke();

                ctx1.beginPath();
                ctx1.moveTo(self.x,self.y);
                ctx1.lineTo(ps[1].x,ps[1].y);
                ctx1.stroke();

                //CODE 
                }
            else {
                console.log(_rad1,_rad2);
                //console.log('non-p',ps[0]);
            }
            /*ctx1.beginPath();
            ctx1.arc(self.x,self.y,2,0,Game.GLOBAL.CIRCLE);
            ctx1.stroke();
            ctx1.closePath();*/



        },
        this.update = function () {
            this.rotate();

        }
    } 

    function Wall (x1,y1,x2,y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
        this.update = function (context1,context2,player) {

        };
        this.draw = function (context1,context2,player) {
            //CODE
        }
    }

    function Vector2D (x,y) {
        this.x = x;
        this.y = y;
        this.dot = function (v) {
             //Dot product
             return v.x*self.x+v.y*self.y;

        }
        this.cross = function (v) {
            //cross product
            return self.x*v.y-self.y*v.x;
        }
    }

    function StraightLine2D (xo,yo,v) {
        if(!(v instanceof(Vector2D)))
        {
            throw new Error('Invalid Argument supplied for constructor "StraightLine2D"');
        }

        this.xo = xo;
        this.yo = yo;
        this.v = v;

        this.calc = function (t) {
            var self = this;
            return { 
                x : t*self.v.x + self.xo,
                y : t*self.v.y + self.yo
            }
        }

        this.intersectionLine = function (R) {
            var self = this;
            var x1 = R.xo;
            var y1 = R.yo;
            var v1 = R.v;
            var _cross = self.v.cross(v1);
            if(_cross == 0)
            {
                return null;
            }

        switch(true) {
                case self.v.x == 0:
                    var t = (self.xo-x1)/v1.x;
                    return R.calc(t);
                break;
                case self.v.y == 0:
                    var t = (self.yo-y1)/v1.y;
                    return R.calc(t);
                break;
                default:
                    var t =  (self.v.y*(self.xo-x1)+self.v.x*(y1-self.yo))/(-_cross);
                    //var t = (t1*(v1.x)+x1-self.xo)/self.v.x;
                    return R.calc(t);
                break;
            }
        }


    }

    function Ray2D (xo,yo,v) {
        if(!(v instanceof(Vector2D)))
        {
            throw new Error('Invalid Argument supplied for constructor "StraightLine2D"');
        }

        this.xo = xo;
        this.yo = yo;
        this.v = v;

        this.calc = function (t) {
            var self = this;

            if(t<0) {
                return null;
            }
            return { 
                x : t*self.v.x + self.xo,
                y : t*self.v.y + self.yo
            }
        }

        this.intersectionLine = function (R) {
            var self = this;
            var x1 = R.xo;
            var y1 = R.yo;

            var v1 = R.v;
            var _cross = self.v.cross(v1);
            if(_cross == 0.0)
            {
                return null;
            }

            switch(true) {
                case self.v.x == 0:
                    var t = (self.xo-x1)/v1.x;
                    return t > 0 ? R.calc(t) : null;
                break;
                case self.v.y == 0:
                    var t = (self.yo-y1)/v1.y;
                    return t > 0 ? R.calc(t) : null;
                break;
                default:


                    var t1 = ((y1-self.yo)*self.v.x+self.v.y*(self.xo-x1))/(v1.x*self.v.y-v1.y*self.v.x);

                    var t = (t1*R.v.x+R.xo-self.xo)/self.v.x;

                    return t >= 0 ? self.calc(t) : null;
                break;
            }
        }

        this.intersectionWall = function (W) {
            var self = this;
            var R = new StraightLine2D(W.x1,W.y1,new Vector2D(W.x1-W.x2,W.y1-W.y2));
            var point = self.intersectionLine(R);


            if(point && 
               point.x <= Math.max(W.x1,W.x2) && point.x >= Math.min(W.x1,W.x2) &&
               point.y <= Math.max(W.y1,W.y2) && point.y >= Math.min(W.y1,W.y2))
            {
                return point;
            }
            return null;
        }

    }

EDIT: I tried to cut out the non-relevant part of the code. Synthesis is not one of my qualities, I hope now is more readable.

user3101803
  • 35
  • 1
  • 7
  • It looks like you have an interesting problem but you probably need to keep working on it narrowing it down before it's ready for a Stack Overflow question. Right now it's hundreds of lines of code including graphics and input handling and mathematics and more. Trimming down to a Minimal, Complete, and Verifiable example will help you understand the problem as well as attract better answers: http://stackoverflow.com/help/mcve – RJHunter Nov 19 '16 at 03:25
  • Some specific tips for your code: When you see you undesired behaviour, what are the mathematical values (inputs and outputs)? Does the bad behaviour persist when you hard-code those input values? (That tells you whether the "user input" section of your code is relevant to your issue.) Does the bad behaviour persist when you hard-code those output values? (That tells you whether the "graphical drawing" section of your code is relevant to your issue). – RJHunter Nov 19 '16 at 03:34
  • Hi, thank you for your answer. I'll trim out the parts of the code as you suggested. I hardcoded input values in a lot of tests, but the bad behaviour persisted. When the bad behaviour appears the output values are undefined and null, as if the intersection point was outside the wall or the intersection point was outside the ray. For other points and angles everything works fine. – user3101803 Nov 19 '16 at 11:17
  • It sounds like you are narrowing down the problem, that's very good! If you keep applying that hard-coding process, you'll trim down the possible places for the bug to a smaller and smaller place. Eventually you will arrive at a small chunk of code -- probably just a couple of lines -- where sensible hardcoded numbers go in, and unwanted numbers come out. At that point, you'll be able to figure it out on your own or you'll have exactly the right information to post on Stack Overflow. – RJHunter Nov 19 '16 at 12:27
  • 1
    I'm not already sure I solved the problem, but I made a fiddle with a lot of intersection test (line with line, line with ray, ray with ray, segment with line, etc..) without thinking to the rest. Seems I'm slowly finding a solution. If not, i'll update again my question. Thank you, sometimes we just need an encouragement! :) – user3101803 Nov 21 '16 at 18:46

0 Answers0