Advertisement
Guest User

Untitled

a guest
Feb 21st, 2019
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 18.12 KB | None | 0 0
  1. // The Sprite class represents a Sprite (Image) on the canvas with which
  2. // you can move and rotate, with absolute or relative amounts or with
  3. // reference to other points or sprites. The direction of the "front" of
  4. // the image can be changed, as can the "hitbox" (collision-triggering
  5. // area). The image can come from a URL, solid block of color, or another
  6. // Sprite, and can be flipped or replaced as needed.
  7.  
  8. // In general, the fields/instance variables for the Sprite class
  9. // starting with _ (underscore) should be regarded as "private" and
  10. // should not be used outside of the class definition.
  11.  
  12. /* CONSTRUCTORS:
  13.  
  14. All constructors create Sprites at (x, y) with size (w, h)
  15. Different constructors are available depending on image source,
  16.   as well as a copy constructor.
  17.  
  18.   Sprite(String url, float x, float y, float w, float h)
  19.     create a Sprite and load its image
  20.     DON'T use this too many times - it will load a new image each time!
  21.     Instead load an image with this once in setup(), then use the copy
  22.       constructor any time you want another copy.
  23.  
  24.   Sprite(float x, float y, float w, float h)
  25.     create a solid black Sprite
  26.  
  27.   Sprite(Sprite s)
  28.     create a copy of Sprite s
  29.  
  30. */
  31.  
  32. /* METHODS/FUNCTIONS:
  33.  
  34. BASIC FUNCTIONS
  35.  
  36.   void display()
  37.     draw the Sprite with its current information
  38.  
  39.   void setSize(float w, float h)
  40.  
  41.   void setCoor(float x, float y)
  42.     set both position coordinates at once
  43.  
  44.   void setX(float x)
  45.  
  46.   void setY(float y)
  47.  
  48.   void setImage(PImage img)
  49.  
  50.   float getX()
  51.  
  52.   float getY()
  53.  
  54.   float getW()
  55.     get the width of the sprite
  56.  
  57.   float getH()
  58.     get the height of the sprite
  59.  
  60.   PImage getImage()
  61.  
  62. TURNING/DIRECTION FUNCTIONS
  63.  
  64.   void turn(float degrees)
  65.     turn the specified number of degrees
  66.  
  67.   void turnToPoint(float x, float y)
  68.     turn to face the specified (x, y) location
  69.  
  70.   void turnToDir(float angle)
  71.     turn to the specified angle
  72.  
  73.   void turnToSprite(Sprite s)
  74.     turn to the specified Sprite s
  75.  
  76.   float getDir()
  77.     get the direction (in degrees) the Sprite is facing
  78.  
  79.   void flip()
  80.     flips Sprite image across its X axis
  81.  
  82. MOVEMENT FUNCTIONS
  83.  
  84.   void moveToPoint(float x, float y)
  85.     move sprite to location (x, y)
  86.  
  87.   void moveToSprite(Sprite s)
  88.     move sprite to location of Sprite s
  89.  
  90.   void moveX(float dx)
  91.     move in the X direction by the specified amount
  92.  
  93.   void moveY(float dy)
  94.     move in the Y direction by the specified amount
  95.  
  96.   void moveXY(float dx, float dy)
  97.     move in both directions by the specified amounts
  98.  
  99.   void forward(float steps)
  100.     move forward in the direction the sprite is facing
  101.     by the specified number of steps (pixels)
  102.  
  103.   void sideStep(float steps)
  104.     move "sideways" relative to the sprite's current facing
  105.     negative steps goes "left", positive goes "right"
  106.  
  107. DISTANCE, LOCATION, AND TOUCHING FUNCTIONS
  108.  
  109.   float distTo(Sprite s)
  110.     calculate the distance from this Sprite to Sprite s
  111.  
  112.   float distTo(float x, float y)
  113.     calculate the distance from this Sprite to (x,y)
  114.  
  115.   boolean touchingSprite(Sprite s)
  116.  
  117.   boolean touchingPoint(float x, float y)
  118.  
  119.   boolean isInsideScreen()
  120.  
  121. HITBOX CONTROL
  122.   (Hitbox: The boundaries of the Sprite for collision checks.)
  123.  
  124.   void displayHitbox()
  125.     shows hitbox as simple red outline - very useful for debugging!
  126.  
  127.   void setRectHitbox(float w, float h)
  128.     set rectangular hitbox of given size
  129.     h is along the front-back axis
  130.     w is along the side-side axis
  131.  
  132.   void resetRectHitbox()
  133.     set rectangular hitbox of size based on image
  134.  
  135.   void setRoundHitbox(float r)
  136.     set circular hitbox of given size
  137.  
  138.   void resetRoundHitbox()
  139.     set circular hitbox based on image size
  140.  
  141.   void setHitboxCenter(float x, float y)
  142.     recenter hitbox relative to center of image
  143.  
  144.   void resetHitboxCenter()
  145.     recenter hitbox to exactly center of image
  146.  
  147.   void setHitboxPoints(PVector[] array)
  148.     set hitbox by individual points, relative to center
  149.       x position of points is along front-back axis
  150.       y position of points is along side-side axis
  151.  
  152. */
  153.  
  154. class Sprite {
  155.   // do not modify these except through the provided methods
  156.   PImage _img;
  157.   float _w;
  158.   float _h;
  159.   float _x;
  160.   float _y;
  161.   PVector _rotVector; // for movement
  162.   float _front = 0;   // angle of front relative to right of image
  163.  
  164.   PVector _hitboxCenter = new PVector();
  165.   PVector[] _hitbox;
  166.  
  167.   boolean _flipped = false;
  168.  
  169.   // constructor to create a Sprite at (x, y) with size (w, h)
  170.   // using the image provided by the url
  171.   Sprite(String url, float x, float y, float w, float h) {
  172.     _img = loadImage(url);
  173.     _x = x;
  174.     _y = y;
  175.     _w = w;
  176.     _h = h;
  177.     _rotVector = new PVector(1, 0, 0);
  178.     resetRectHitbox();
  179.   }
  180.  
  181.   // constructor to create a Sprite at (x, y) with size (w, h)
  182.   // with a solid black color. The color of this Sprite can
  183.   // change using the setColor() function
  184.   Sprite(float x, float y, float w, float h) {
  185.     _img = createImage(1, 1, RGB);
  186.     _x = x;
  187.     _y = y;
  188.     _w = w;
  189.     _h = h;
  190.     _rotVector = new PVector(1, 0, 0);
  191.     resetRectHitbox();
  192.   }
  193.  
  194.   // constructor to create a copy of Sprite s
  195.   Sprite(Sprite s) {
  196.     _img = s._img;
  197.     _x = s._x;
  198.     _y = s._y;
  199.     _w = s._w;
  200.     _h = s._h;
  201.     _rotVector = new PVector(s._rotVector.x, s._rotVector.y, 0);
  202.     _front = s._front;
  203.     _hitboxCenter = new PVector(s._hitboxCenter.x, s._hitboxCenter.y);
  204.     _hitbox = new PVector[s._hitbox.length];
  205.     for (int i = 0; i < _hitbox.length; i++) {
  206.         _hitbox[i] = new PVector(s._hitbox[i].x, s._hitbox[i].y);
  207.     }
  208.     _flipped = s._flipped;
  209.   }
  210.  
  211.   // adjust the direction of the PImage of the Sprite
  212.   // without changing the orientation of the Sprite
  213.   void frontAngle(float degrees) {
  214.     float newFront = radians(degrees);
  215.  
  216.     // movement done from this direction from now on
  217.     _rotVector.rotate(newFront - _front);
  218.  
  219.     _front = newFront;
  220.   }
  221.  
  222.   // set rectangular hitbox of given size
  223.   // h is along the front-back axis
  224.   // w is along the perpendicular axis
  225.   void setRectHitbox(float w, float h) {
  226.     _hitbox = new PVector[]{
  227.       new PVector(-w/2, h/2),
  228.       new PVector(-w/2, -h/2),
  229.       new PVector(w/2, -h/2),
  230.       new PVector(w/2, h/2)
  231.     };
  232.   }
  233.  
  234.   // set rectangular hitbox of size based on image
  235.   void resetRectHitbox() {
  236.     setRectHitbox(_w, _h);
  237.   }
  238.  
  239.   // set circular hitbox of given size
  240.   void setRoundHitbox(float r) {
  241.     _hitbox = new PVector[]{new PVector(r, r*2)};
  242.   }
  243.  
  244.   // set circular hitbox based on image size
  245.   void resetRoundHitbox() {
  246.     setRoundHitbox((_w+_h)/4);
  247.   }
  248.  
  249.   // recenter hitbox relative to center of image
  250.   void setHitboxCenter(float x, float y) {
  251.     _hitboxCenter = new PVector(x, y);
  252.   }
  253.  
  254.   // recenter hitbox to exactly center of image
  255.   void resetHitboxCenter() {
  256.     _hitboxCenter = new PVector(0, 0);
  257.   }
  258.  
  259.   void setHitboxPoints(PVector[] array) {
  260.     if (array.length > 0) {
  261.       boolean valid = true;
  262.       for (PVector pv : array) if (pv == null) valid = false;
  263.  
  264.       if (valid) _hitbox = array;
  265.       else println("invalid hitbox: " + java.util.Arrays.toString(array));
  266.     }
  267.     else {
  268.       println("hitbox must have 3+ points: " + java.util.Arrays.toString(array));
  269.     }
  270.   }
  271.  
  272.   // change the color of a Sprite created without an image
  273.   void setColor(float r, float g, float b) {
  274.     color c = color(r, g, b);
  275.     for(int x = 0; x < _img.width; x++) {
  276.       for(int y = 0; y < _img.height; y++) {
  277.         _img.set(x, y, c);
  278.       }
  279.     }
  280.   }
  281.  
  282.   // flips Sprite image across its X axis
  283.   void flip() {
  284.       _flipped = !_flipped;
  285.   }
  286.  
  287.   // turn the specified number of degrees
  288.   void turn(float degrees) {
  289.     _rotVector.rotate(radians(degrees));
  290.   }
  291.  
  292.   // turn to the specified (x, y) location
  293.   void turnToPoint(float x, float y) {
  294.     _rotVector.set(x - _x, y - _y, 0);
  295.     _rotVector.setMag(1);
  296.   }
  297.  
  298.   // turn to the specified angle
  299.   void turnToDir(float angle) {  
  300.     float radian = radians(angle);
  301.     _rotVector.set(cos(radian), sin(radian));
  302.     _rotVector.setMag(1);
  303.   }
  304.  
  305.   // turn to the specified Sprite s
  306.   void turnToSprite(Sprite s) {
  307.     turnToPoint(s._x, s._y);
  308.   }
  309.  
  310.   // move sprite to location (x, y)
  311.   void moveToPoint(float x, float y) {
  312.     _x = x;
  313.     _y = y;
  314.   }
  315.  
  316.   // move sprite to location of Sprite s
  317.   void moveToSprite(Sprite s) {
  318.     _x = s._x;
  319.     _y = s._y;
  320.   }
  321.  
  322.   // move in the X direction by the specified amount
  323.   void moveX(float x) {
  324.     _x += x;
  325.   }
  326.  
  327.   // move in the Y direction by the specified amount
  328.   void moveY(float y) {
  329.     _y += y;
  330.   }
  331.  
  332.   void moveXY(float dx, float dy) {
  333.       _x += dx;
  334.       _y += dy;
  335.   }
  336.  
  337.   // move forward in the direction the sprite is facing
  338.   // by the specified number of steps (pixels)
  339.   void forward(float steps) {
  340.     _x += _rotVector.x * steps;
  341.     _y += _rotVector.y * steps;
  342.   }
  343.  
  344.   // move 90 degree clockwise from the direction
  345.   // the sprite is facing by the specified number of steps (pixels)
  346.   void sideStep(float steps) {
  347.     _rotVector.rotate(PI / 2);
  348.     _x += _rotVector.x * steps;
  349.     _y += _rotVector.y * steps;
  350.     _rotVector.rotate(-PI / 2);
  351.   }
  352.  
  353.   // draw the Sprite. This function
  354.   // should be called in the void draw() function
  355.   void display() {
  356.     pushMatrix();
  357.     pushStyle();
  358.  
  359.     translate(_x, _y);
  360.     rotate(_rotVector.heading() - _front);
  361.     if (_flipped) scale(-1, 1);
  362.     imageMode(CENTER);
  363.     image(_img, 0, 0, _w, _h);
  364.  
  365.     popStyle();
  366.     popMatrix();
  367.   }
  368.  
  369.   void displayHitbox() {
  370.     PVector cen = _getCenter();
  371.  
  372.     pushStyle();
  373.     stroke(255, 0, 0);
  374.     strokeWeight(5);
  375.     noFill();
  376.  
  377.     if (_hitbox.length == 1) {
  378.       ellipseMode(CENTER);
  379.       ellipse(cen.x, cen.y, _hitbox[0].y, _hitbox[0].y);
  380.     }
  381.     else {
  382.       PVector[] corners = _getPoints();
  383.       for (int i = 0; i < corners.length; i++) {
  384.         PVector a = corners[i];
  385.         PVector b = corners[(i+1)%corners.length];
  386.         line(a.x, a.y, b.x, b.y);
  387.       }
  388.     }
  389.  
  390.     line(cen.x, cen.y, cen.x + _rotVector.x * 20, cen.y + _rotVector.y * 20);
  391.  
  392.     fill(255,0,0);
  393.     noStroke();
  394.     ellipse(cen.x, cen.y, 15, 15);
  395.  
  396.     popStyle();
  397.   }
  398.  
  399.   // set the size of the Sprite
  400.   void setSize(float w, float h) {
  401.     _w = w;
  402.     _h = h;
  403.   }
  404.  
  405.   void setCoor(float x, float y) {
  406.     _x = x;
  407.     _y = y;
  408.   }
  409.  
  410.   // set the x coordinate
  411.   void setX(float x) {
  412.     _x = x;
  413.   }
  414.  
  415.   // set the y coordinate
  416.   void setY(float y) {
  417.     _y = y;
  418.   }
  419.  
  420.   // change the image of the Sprite
  421.   void setImage(PImage img) {
  422.     _img = img;
  423.   }
  424.  
  425.   // get the x coordinate of the sprite
  426.   float getX() {
  427.     return _x;
  428.   }
  429.  
  430.   // get the y coordinate of the sprite
  431.   float getY() {
  432.     return _y;
  433.   }
  434.  
  435.   // get the width of the sprite
  436.   float getW() {
  437.     return _w;
  438.   }
  439.  
  440.   // get the height of the sprite
  441.   float getH() {
  442.     return _h;
  443.   }
  444.  
  445.   // get the image of the sprite
  446.   PImage getImage() {
  447.     return _img;
  448.   }
  449.  
  450.   // get the direction (in degrees) the Sprite is facing
  451.   float getDir() {
  452.     return degrees(_rotVector.heading());
  453.   }
  454.  
  455.   // calculate the distance from this Sprite to Sprite s
  456.   float distTo(Sprite s) {
  457.     return dist(_x, _y, s._x, s._y);
  458.   }
  459.  
  460.   float distToPoint(float x, float y) {
  461.     return dist(_x, _y, x, y);
  462.   }
  463.  
  464.   // checks whether this Sprite is touching Sprite s
  465.   boolean touchingSprite(Sprite s) {
  466.     if (s._hitbox.length == 1) {
  467.       if (_hitbox.length == 1) {
  468.         return PVector.dist(this._getCenter(), s._getCenter()) <=
  469.                this._hitbox[0].x + s._hitbox[0].x;
  470.       }
  471.       return _circPoly(s._getCenter(), s._hitbox[0].x, this._getPoints());
  472.     }
  473.     if (_hitbox.length == 1) {
  474.       return _circPoly(this._getCenter(), this._hitbox[0].x, s._getPoints());
  475.     }
  476.  
  477.     PVector[] s1Points = s._getPoints();
  478.     PVector[] s2Points = this._getPoints();
  479.  
  480.     for(int i = 0; i < s1Points.length; i++) {
  481.       PVector a = s1Points[i], b = s1Points[(i+1)%s1Points.length];
  482.       for(int j = 0; j < s2Points.length; j++) {
  483.         PVector c = s2Points[j], d = s2Points[(j+1)%s2Points.length];
  484.  
  485.         // sprites touch if ab crosses cd
  486.         if(_clockwise(a, c, d) != _clockwise(b, c, d) &&  // a & b on different sides of cd, and
  487.            _clockwise(a, b, c) != _clockwise(a, b, d)) {  // c & d on different sides of ab
  488.           return true;
  489.         }
  490.       }
  491.     }
  492.  
  493.     return _insidePts(s1Points,s2Points) || _insidePts(s2Points,s1Points);
  494.   }
  495.  
  496.   //checks to see if this Sprite is fully inside another sprite
  497.   boolean insideSprite(Sprite s){
  498.     if (s._hitbox.length == 1) {
  499.       if (_hitbox.length == 1) {
  500.         return PVector.dist(s._getCenter(),this._getCenter()) <
  501.                s._hitbox[0].x - this._hitbox[0].x;
  502.       }
  503.       return _insideCirc(_getPoints(), s._getCenter(), s._hitbox[0].x);
  504.     }
  505.     if (s._hitbox.length == 1) {
  506.       // TODO: check if center is in middle but NOT touching any side
  507.       //   (will want to adapt existing _circPoly to separate side-touching
  508.       //    code into individual method)
  509.       return false;
  510.     }
  511.     return _insidePts(this._getPoints(), s._getPoints());
  512.   }
  513.  
  514.   // checks whether this Sprite is touching the specified point
  515.   boolean touchingPoint(float x, float y) {
  516.     if (_hitbox.length == 1) return dist(x,y,_hitboxCenter.x, _hitboxCenter.y) < _hitbox[0].x;
  517.     return _ptPoly(new PVector(x,y), _getPoints());
  518.   }
  519.  
  520.   // checks whether this Sprite's hitbox is at least partially inside the canvas
  521.   // TODO: technically this returns true even if circular Sprite is just outside
  522.   //   at the corners, and false if a tilted rectangular Sprite's edge crosses
  523.   //   a corner with endpoints outside
  524.   boolean isInsideScreen() {
  525.     if (_hitbox.length == 1) {
  526.       float r = _hitbox[0].x;
  527.       PVector c = _getCenter();
  528.       return 0 <= c.x + r && c.x - r < width && 0 <= c.y + r && c.y - r < height;
  529.     }
  530.  
  531.     PVector[] points = this._getPoints();
  532.     for(PVector p : points) {
  533.       if(0 <= p.x && p.x < width && 0 <= p.y && p.y < height) {
  534.         return true;
  535.       }
  536.     }
  537.     return false;
  538.   }
  539.  
  540.   PVector out = new PVector(-10000, -10000); // any outside point
  541.  
  542.   // (pseudo-static) checks whether pt touches polygon
  543.   boolean _ptPoly(PVector pt, PVector[] poly) {  
  544.     // count edges crossed by the line connecting the target point to "the outside"
  545.     int count = 0;
  546.  
  547.     for(int i = 0; i < poly.length; i++) {
  548.       PVector a = poly[i], b = poly[(i+1)%poly.length];  // edge points
  549.       if(_clockwise(a, pt, out) != _clockwise(b, pt, out) &&  // a & b on different sides of line
  550.          _clockwise(a, b, pt)   != _clockwise(a, b, out)) {   // tgt & out on diff sides of edge
  551.         count++;
  552.       }
  553.     }
  554.  
  555.     return count % 2 == 1;
  556.     // a convex poly would be crossed on one edge;
  557.     //   concave could be crossed on any odd # of edges
  558.   }
  559.  
  560.   // (pseudo-static) checks whether circle is touching polygon
  561.   //   (including one inside the other)
  562.   boolean _circPoly(PVector center, float r, PVector[] poly) {
  563.     // center is in polygon
  564.     if (_ptPoly(center, poly)) return true;
  565.     if (_insideCirc(poly, center, r)) return true;
  566.  
  567.     // circle encloses any corner
  568.     for (PVector corner : poly) {
  569.       if (dist(center.x, center.y, corner.x, corner.y) < r) return true;
  570.     }
  571.  
  572.     // circle is adjacent and close enough to any side
  573.     for (int i = 0; i < poly.length; i++) {
  574.       if (_circSeg(center, r, poly[i], poly[(i+1)%poly.length])) return true;
  575.     }
  576.  
  577.     return false;
  578.   }
  579.  
  580.   // (pseudo-static)
  581.   // checks if circle touches segment AB from a perpendicular direction,
  582.   //   but NOT from "beyond the ends"
  583.   //   (this should be checked separately if desired)
  584.   // aka, checks if center forms a perpendicular to any point on segment
  585.   //   with length <= r
  586.   boolean _circSeg(PVector center, float r, PVector a, PVector b) {
  587.     PVector ab = PVector.sub(b,a);
  588.     PVector abPerp = (new PVector(-ab.y, ab.x)).normalize().mult(r);
  589.  
  590.     PVector[] limits = new PVector[]{
  591.       PVector.add(a,abPerp), // move perpendicular to the segment by
  592.       PVector.sub(a,abPerp), // distance r from each of the endpoints,
  593.       PVector.sub(b,abPerp), // forming a bounding rectangle
  594.       PVector.add(b,abPerp)
  595.     };
  596.  
  597.     return _ptPoly(center, limits);
  598.   }
  599.  
  600.   // (pseudo-static) checks whether all inPts are completely within the outPts
  601.   //   TODO: does not check whether edges between inPts are within outPts!
  602.   boolean _insidePts(PVector[] inPts, PVector[] outPts) {
  603.  
  604.     for(int i = 0; i < inPts.length; i++){
  605.       // direction of angular relationship to any side must match
  606.       //   direction of relationship to opposite side
  607.       if(!_ptPoly(inPts[i], outPts)) return false;
  608.     }
  609.     return true;
  610.   }
  611.  
  612.   // (pseudo-static) checks whether all inPts are completely within circle
  613.   boolean _insideCirc(PVector[] inPts, PVector center, float r) {
  614.  
  615.     for(int i = 0; i < inPts.length; i++){
  616.       // direction of angular relationship to any side must match
  617.       //   direction of relationship to opposite side
  618.       if(PVector.dist(inPts[i],center) > r) return false;
  619.     }
  620.     return true;
  621.   }
  622.  
  623.   // get hitbox absolute center based on image center, relative offset, rotation, and front
  624.   PVector _getCenter() {
  625.     PVector cen = new PVector(_hitboxCenter.x, _hitboxCenter.y);
  626.     cen.rotate(_rotVector.heading() - _front);
  627.     cen.x += _x;
  628.     cen.y += _y;
  629.     return cen;
  630.   }
  631.  
  632.   // get points representing rectangular hitbox
  633.   PVector[] _getPoints() {
  634.     PVector cen = _getCenter();
  635.  
  636.     PVector[] points = new PVector[_hitbox.length];
  637.     float angle = _rotVector.heading();
  638.     for(int i = 0; i < _hitbox.length; i++) {
  639.       points[i] = new PVector(_hitbox[i].x, _hitbox[i].y);
  640.       points[i].rotate(angle);
  641.       points[i].x += cen.x;
  642.       points[i].y += cen.y;
  643.     }
  644.     return points;
  645.   }
  646.  
  647.   // checks whether motion in AB turns clockwise to follow BC
  648.   // i.e. which way is angle ABC concave?
  649.   private boolean _clockwise(PVector A, PVector B, PVector C) {
  650.     return (C.y-A.y) * (B.x-A.x) > (B.y-A.y) * (C.x-A.x);
  651.   }
  652. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement