Advertisement
Guest User

Sprite class for Processing

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