Snap.svg - Drag to select multiple items - select

I was just wondering if there is an existing open source Snap.svg class already built to drag and select multiple elements? Looking for something like the jQuery drag select.
Like this: JSFiddle
$(".itemlist").selectable({filter:"li"});

I found an example for this using Raphael JS and I converted it to work with Snap. Here is a codepen.
http://codepen.io/fractorr/pen/vOzaOx/
$(document).ready(function() {
var s = Snap("#svg");
//make an object in the background on which to attach drag events
var mat = s.rect(0, 0, 300, 300).attr("fill", "#ffffff");
var circle = s.circle(75, 75, 50);
var rect = s.rect(150, 150, 50, 50);
var set = s.g(circle, rect);
set.attr({
fill: 'red',
stroke: 0
});
var box;
//set that will receive the selected items
var selections = s.group();
//DRAG FUNCTIONS
//when mouse goes down over background, start drawing selection box
function dragstart (x, y, event) {
box = s.rect(x, y, 0, 0).attr("stroke", "#9999FF");
}
//when mouse moves during drag, adjust box. If to left or above original point, you have to translate the whole box and invert the dx or dy values since .rect() doesn't take negative width or height
function dragmove (dx, dy, x, y, event) {
var xoffset = 0,
yoffset = 0;
if (dx < 0) {
xoffset = dx;
dx = -1 * dx;
}
if (dy < 0) {
yoffset = dy;
dy = -1 * dy;
}
box.transform("T" + xoffset + "," + yoffset);
box.attr("width", dx);
box.attr("height", dy);
box.attr("fill", "none");
}
function dragend (event) {
//get the bounds of the selections
var bounds = box.getBBox();
box.remove();
reset();
var items = set.selectAll("*");
items.forEach(function(el) {
//here, we want to get the x,y vales of each object regardless of what sort of shape it is, but rect uses rx and ry, circle uses cx and cy, etc
//so we'll see if the bounding boxes intercept instead
var mybounds = el.getBBox();
//do bounding boxes overlap?
//is one of this object's x extremes between the selection's xextremes?
if (Snap.path.isBBoxIntersect(mybounds, bounds)) {
selections.append(el);
}
});
selections.attr("opacity", 0.5);
}
function reset () {
//empty selections and reset opacity;
var items = selections.selectAll("*");
items.forEach(function(el) {
set.append(el);
});
}
mat.drag(dragmove, dragstart, dragend);
});

Related

Mask out color in Image canvas flutter

I am trying to add skin tone to an image in flutter canvas.
I've used the following code before to apply chroma by altering pixels when I load the image:
static Future<ui.Image> applyChromaToImage(ui.Image image, String pathForImage, RGBPixel chromaToApply, {RGBPixel previousChromaColor}) async
{
List<ChromaPointRange> chromaPoints = processChromaImageBytes(
image, imgBytes);
for(ChromaPointRange rnge in chromaPoints)
{
for(int y = rnge.yValStart; y <= rnge.yValEnd; y++)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, rnge.xVal, y);
//replace current pixel with skin tone
RGBPixel newPixl = currentPixel.mergeSkinTonePixel(chromaToApply, previousChromaColor);
imgBytes.setUint32((y * image.width + rnge.xVal) * 4, newPixl.getHex());
}
}
final Completer<ui.Image> imageCompleter = new Completer();
//looks the endian format doesn't get set right here
ui.PixelFormat formatToUse = Endian.host == Endian.little ? ui.PixelFormat.rgba8888 : ui.PixelFormat.bgra8888;
ui.decodeImageFromPixels(
imgBytes.buffer.asUint8List(),
image.width,
image.height,
formatToUse,
(ui.Image result) {
imageCompleter.complete(result);
// use your result image
},
);
//return image;
return await imageCompleter.future;
}
static List<ChromaPointRange> processChromaImageBytes(ui.Image image, ByteData imgBytes)
{
List<ChromaPointRange> chromaPoints = [];
ChromaPointRange currentPoints = null;
for(int x = 0; x < image.width; x = x + 1)
{
for(int y = 0; y < image.height; y = y + 1)
{
RGBPixel currentPixel = RGBPixel.generatePixelFromImagePos(imgBytes, image.width, x, y);
if(currentPixel.isSkinTonePixel())
{
if(currentPoints == null)
{
currentPoints = ChromaPointRange.fromEmpty();
currentPoints.xVal = x;
currentPoints.yValStart = y;
}
}
else if(currentPoints != null)
{
currentPoints.yValEnd = y - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
if(currentPoints != null)
{
currentPoints.yValEnd = image.height - 1;
chromaPoints.add(currentPoints);
currentPoints = null;
}
}
return chromaPoints;
}
which basically checks every pixel in the image to see if it's within a range of the target color ( with is RGB 0, 255, 0), then adjusts the pixel if it is. This works, but takes a really long time ~ 3 seconds for a 1920 x 1080 image.
The end result is that I want to paint the image to a canvas with a skin tone applied. I've tried a different strategy, by painting the color underneath the image, and then trying to mask out that color from the image using canvas color filters. This is 1000% faster, but doesn't quite work.
Here is the code:
renderSprite(Canvas canvasToRender, Offset offsetToRender)
{
Paint imgPaint = new Paint();
if(chromaToApply != null)
{
Paint chromaPaint = new Paint();
chromaPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(chromaToApply.redVal, chromaToApply.greenVal, chromaToApply.blueVal, 1), BlendMode.modulate);
canvasToRender.drawImage(spriteImage, offsetToRender, chromaPaint);
imgPaint.colorFilter = ColorFilter.mode(Color.fromRGBO(0, 255, 0, 1), BlendMode.dstOut);
}
if(spriteImage != null)
canvasToRender.drawImage(spriteImage, offsetToRender, imgPaint);
}
Here is the image that is painted underneath
Here is the image that is painted ontop
So I'm trying to mask out the green so the tan skin tone shows through on specific parts of the image.
I can't seem to find any combination of ColorFilter or anything else that will mask out the green color for me from the canvas. Any suggestions?

Objects in unity scene and game view are disappearing when I adjust my view

So I'm trying to learn Unity DOTS and I've built out a program that renders a bunch of cubes. But for some reason, in both the game view and the scene view, when I move the camera around, the cubes just at the edge of the screen start to disappear. I don't know if maybe there's some setting that causes non-visible objects to not be rendered or something or maybe it's something to do with them being entities?
This is the code I'm using to spawn the tiles:
private void SpawnTiles()
{
EntityArchetype tileArchetype = entityManager.CreateArchetype(
typeof(Translation),
typeof(Rotation),
typeof(RenderMesh),
typeof(RenderBounds),
typeof(LocalToWorld),
typeof(TileComponent)
);
for (float i = 0f; i < gridX; i++)
{
for (float j = 0f; j < gridY; j++)
{
MakeTile(tileArchetype, new float3(i, 0.0f, j));
}
}
}
private void MakeTile(EntityArchetype tileArchetype, float3 translation)
{
Entity tile = entityManager.CreateEntity(tileArchetype);
entityManager.AddComponentData(tile, new Translation
{
Value = translation
});
entityManager.AddComponentData(tile, new TileComponent
{
x = (int) translation.x,
y = (int) translation.y,
z = (int) translation.z,
isOccupied = false,
isTraversable = false,
index = CalculateIndex((int) translation.x, (int) translation.y, (int) translation.z),
cameFromTileIndex = -1
});
entityManager.AddSharedComponentData(tile, new RenderMesh
{
mesh = tileMesh,
material = tileMaterial
});
}
Screenshot showing clipping
This happen when RenderBounds value is left at default(AABB) as it has 0 size/extent hence making your mesh a mere point for the frustum culling system.

How to limit player's velocity only when it is accelerating?

I have a 2.5D space shooter game in progress that uses Unity's built-in physics. Everything happens in 2D space but all the models are 3D.
The player (a space ship) can rotate using a controller axis and can accelerate when a button is held down (e.g. xbox controller's A button).
There is a limit on how fast the player can move (maxEngineSpeed) and I clamp the magnitude of the RigidBody's velocity in FixedUpdate as follows:
if (rb.velocity.magnitude > maxEngineSpeed)
{
rb.velocity = Vector2.ClampMagnitude(rb.velocity, maxEngineSpeed);
}
Now the problem is that this prevents the veclocity from ever reaching a value higher than maxEngineSpeed .
I want a behaviour that only limits the velocity when the player is accelerating. If the player somehow gains more speed from a coillision or from a bullet hit, then the velocity should not be limited. We can think it like the space ship not having enough power in its engines to go any faster. It's like linear drag but only when accelerating (when not accelerating, the ship doesn't decelerate at all). I have power-ups that grant the player more maximum speed, so it's important.
How would this be implemented? I've tried to only limit the velocity when the player is accelerating, but then it clamps it immediately to specified value and it looks unnatural. Would a couroutine work that would slowly reduce the magnitude when accelerating? But then it would have to take account the direction of the player and current velocity?
EDIT: Clarification: in practise what I would like to have is to ask a RigidBody "if I apply this force to you while you're moving faster than maxEngineSpeed, would it increase your speed? If it would, don't apply the force, if it would decrease your speed, then apply it".
EDIT: changed the maxSpeed variable name to maxEngineSpeed for more clarity.
Remove the clamping in FixedUpdate. Instead, add a check where you add Velocity (where you detect Xbox Controllers 'A' is pressed).
Something like:
if(Input.GetButton("Xbox-A"))
{
if(rb.velocity.magnitude < scaledMaxSpeed)
{
rb.addForce(...);
}
}
So if you are faster than your max-speed, the ship cannot accelerate more (by own power).
Pull down & Drag
There are so many ways to achieve what you want. Below I show two possible methods with a working demo to allow you to get a bit of a feel for how the perform and differ. Also link at bottom to another demo.
Pull down
You can pull down the velocity by defining a max over speed and a over speed drag coefficient
The pull down method
Define settings
float pullDown = 0.1f; // dimensionless > 0, < 1
float maxOverSpeed = 5.0f;
float maxSpeed = 4.0f
float acceleration = 0.1f;
Per frame
if (accelerate && speed < maxSpeed) { speed += acceleration }
// clamp max over speed
speed = speed > maxOverSpeed ? maxOverSpeed : speed;
float speedAdjust = speed - maxSpeed;
// pull speed down if needed
speed -= speedAdjust > 0.0f ? speedAdjust * pullDown : 0.0f;
// set the velocity magnitude to the new speed
Personally I don't like this method as it is a coasting model, ship gets to speed an holds it, there is no deceleration, but it does give finer control over velocity.
Drag
My preferred method is to use a simple drag coefficient. Slight modification to add extra draw when over speed
However this makes is difficult to know what the max speed will be given some acceleration. There is a formula that will give you a drag coefficient to match a max speed for acceleration, or acceleration to match a max speed for a drag coefficient, but off the top of my head I can not remember it as its been years since I found I needed to use it.
I wing it and define an approximation, test it, and refine till I get what feels right. In reality if ask what is the max speed of the player? All i know is not too fast and not too slow. :P
The drag method
Define
float acceleration = 0.1f;
float drag = 1.0f - 0.021f;
float overSpeedDrag = 1.0f - 0.026f;
float maxSpeed = 4;
Per frame
// apply drag depending on speed
speed *= speed > maxSpeed ? overSpeedDrag : drag;
if (accelerate) { speed += acceleration }
// set the velocity magnitude to the new current speed
Example
These methods as code do not give much of a feel for the actual results so the following snippet implements both methods so you can see and feel how they work.
The code is at the top (in JavaScript) the two different methods are flagged PULL_DOWN, DRAG in the function update() {
Ship speeds are in pixels per second (Pps)
Both ships have same acceleration constant, however ship B (drag method) does not accelerate at a constant rate.
Ship A will coast, ship B will always come to a stop.
Click bump to kick the ship's velocity.
const accelFunction = {
get vel() { return new Vec2(0, 0) },
speed: 0,
acceleration: 0.1,
maxSpeed: 4,
// drag constants
drag: 1 - 0.0241,
overSpeedDrag: 1 - 0.0291,
// pulldown constants;
overSpeed: 5,
pullDown: 0.1,
update() {
if (this.method === DRAG) { // Drag method
this.speed *= this.speed > this.maxSpeed ? this.overSpeedDrag: this.drag;
if (this.accelerate) { this.speed += this.acceleration }
} else { // Pull down method
if (this.accelerate && this.speed < this.maxSpeed) { this.speed += this.acceleration }
this.speed = this.speed > this.maxOverSpeed ? this.maxOverSpeed : this.speed;
var speedAdjust = this.speed - this.maxSpeed;
this.speed -= speedAdjust > 0 ? speedAdjust * this.pullDown : 0;
}
// move ship
this.vel.length = this.speed;
this.pos.add(this.vel);
},
}
/* rest of code unrelated to anwser */
requestAnimationFrame(start);
const ctx = canvas.getContext("2d");
const PULL_DOWN = 0;
const DRAG = 1;
var shipA, shipB;
var bgPos;
function Ship(method, control, controlBump) { // creates a Player ship
control.addEventListener("mousedown",() => API.go());
control.addEventListener("mouseup",() => API.coast());
control.addEventListener("mouseout",() => API.coast());
controlBump.addEventListener("click",() => API.bump());
const API = {
...accelFunction,
pos: new Vec2(100, 50 + method * 50),
method, // 0 drag method, 1 pulldown
draw() {
ctx.setTransform(1,0,0,1,this.pos.x - bgPos.x, this.pos.y)
ctx.strokeStyle = "#FFF";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.lineTo(20, 0);
ctx.lineTo(-20, -20);
ctx.lineTo(-20, 20);
ctx.closePath();
ctx.stroke();
ctx.fillText(this.method ? "B" : "A", -11, 3);
ctx.fillText((this.speed * 60 | 0) + "Pps", 80, 3);
if (this.accelerate) {
ctx.strokeStyle = "#FF0";
ctx.beginPath();
ctx.lineTo(-20, -10);
ctx.lineTo(-30 - Math.rand(0,10), 0);
ctx.lineTo(-20, 10);
ctx.stroke();
}
},
focus: false,
reset() {
this.focus = false;
this.vel.zero();
this.pos.init(100, 50 + this.method * 50);
this.speed = 0;
this.accelerate = false;
},
go() {
this.accelerate = true;
this.focus = true;
if (this.method === 1) { shipA.reset() }
else { shipB.reset() }
},
coast() {
this.accelerate = false;
},
bump() {
this.speed += 1;
},
};
return API;
}
function start() {
init();
requestAnimationFrame(mainLoop);
}
function mainLoop() {
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,500,170);
shipA.update();
shipB.update();
bgPos.x = shipA.focus ? shipA.pos.x - 50 : shipB.pos.x - 50 ;
drawBG(bgPos);
shipA.draw();
shipB.draw();
requestAnimationFrame(mainLoop);
}
function drawBG(bgPos) {
ctx.fillStyle = "#FFF";
ctx.beginPath();
const bgX = -bgPos.x + 100000;
for (const p of background) {
x = (p.x + bgX) % 504 - 2;
ctx.rect(x, p.y, 2, 2);
}
ctx.fill();
}
const BG_COUNT = 200;
const background = [];
function init() {
ctx.font = "16px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
bgPos = new Vec2();
shipA = Ship(PULL_DOWN, goA, bumpA);
shipB = Ship(DRAG, goB, bumpB);
var i = BG_COUNT;
while (i--) {
background.push(new Vec2(Math.rand(0, 10000), Math.rand(-1, 170)));
}
}
/* Math LIB Vec2 and math extensions */
Math.rand = (m, M) => Math.random() * (M - m) + m;
function Vec2(x = 0, y = (temp = x, x === 0 ? (x = 0 , 0) : (x = x.x, temp.y))) { this.x = x; this.y = y }
Vec2.prototype = {
init(x, y = (temp = x, x = x.x, temp.y)) { this.x = x; this.y = y; return this },
zero() { this.x = this.y = 0; return this },
add(v, res = this) { res.x = this.x + v.x; res.y = this.y + v.y; return res },
scale(val, res = this) { res.x = this.x * val; res.y = this.y * val; return res },
get length() { return this.lengthSqr ** 0.5 },
set length(l) {
const len = this.lengthSqr;
len > 0 ? this.scale(l / len ** 0.5) : (this.x = l);
},
get lengthSqr() { return this.x * this.x + this.y * this.y },
};
canvas {background: #347;}
div {
position: absolute;
top: 150px;
left: 20px;
}
span { color: white; font-family: arial }
<canvas id="canvas" width="500" height="170"></canvas>
<div>
<button id="goA">Go A</button>
<button id="bumpA">Bump A</button>
<button id="goB">Go B</button>
<button id="bumpB">Bump B</button>
<span> Methods: A = Pull down B = Drag </span>
</div>
No limit
There are many variations on these methods, and the are many example on SO (I have written many answers in the subject. eg See demo snippet (bottom of answer) for example of drag method modification) .
Which method you use is very dependent on how you want the interaction to feel, there is no right or wrong method as game physics will is very different than real physics.
Knowing that acceleration (a) is the change in velocity (Δv) over the change in time (Δt), I'll check that.
Maybe with something like (pseudo):
float lastVelocity = 0;
bool isAccelerating = false;
Update()
{
float currentVelocity = rb.velocity;
if(currentVelocity > lastVelocity)
{
isAccelerating = true;
lastVelocity = currentVelocity;
}
else
{
isAccelerating = false;
}
}
Now you know when your "ship" is speedingUp, the only way to decrease the speed is caused by external forces (like gravity, or friction), depending of your setup, I'll deactivate those forces, or change the physicalMaterial that is causing the friction.

How to get radius of a Circle Shape after I scaled it [JavaFX]

I'm trying to get the radius of a Circle Shape after I scaled it. I don't know how to do it. I'm developing a software (with javaFX library) that will let the user add Shapes (Circle, Rectangle, Squares,...)to a Panel, simply "modify" them, save the drawing and load it. I'm managing the saving/loading stuff by cycling through the Nodes the user added (there's a button to create Circle, Rectangle, ... and add it to the Panel), taking the properties I need to load them (centerX, centerY, scaleX, scaleY, ...) and, when button "save" pressed, to save them in a txt file. (stil working on loading, but my thought was to read the previous txt file and cycle it to re-create Shapes on panel in the same position, scale, rotation.)
Here's the code I wrote this is the portion of code that create circle:
public Circle createCircle(double x, double y, double r, Color color) {
Circle circle = new Circle(x, y, r, color);
circle.setCursor(Cursor.HAND);
circle.setOnMousePressed((t) -> {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
Circle c = (Circle) (t.getSource());
c.toFront();
});
circle.setOnMouseDragged((t) -> {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
Circle c = (Circle) (t.getSource());
c.setCenterX(c.getCenterX() + offsetX);
c.setCenterY(c.getCenterY() + offsetY);
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
});
return circle;
}
Here's the code i use to scale it:
public void addMouseScrolling(Node node) {
node.setOnScroll((ScrollEvent event) -> {
// Adjust the zoom factor as per your requirement
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0){
zoomFactor = 2.0 - zoomFactor;
}
node.setScaleX(node.getScaleX() * zoomFactor);
node.setScaleY(node.getScaleY() * zoomFactor);
});
}
And this is the code of the save button:
Button btn2 = new Button("Save");
btn2.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
int i=0;
for (Node node: root.getChildren()) i++;
String stringa_salvataggio[] = new String[i];
String stringa="";
i=0;
String scal_X_s, scal_Y_s, Lyt_X_s, Lyt_Y_s, width_s, height_s;
for (Node node: root.getChildren()){
String stringa_nodo = node.toString();
Double scal_X = node.getScaleX();
scal_X_s = Double.toString(scal_X);
Double scal_Y = node.getScaleY();
scal_Y_s = Double.toString(scal_X);
Double Lyt_X = node.getLayoutX();
Lyt_X_s = Double.toString(scal_X);
Double Lyt_Y = node.getLayoutY();
Lyt_Y_s = Double.toString(scal_X);
Double width = node.getLayoutBounds().getWidth();
width_s = Double.toString(scal_X);
Double height = node.getLayoutBounds().getHeight();
height_s = Double.toString(scal_X);
stringa = stringa + scal_X_s + scal_Y_s + "\n";
stringa_salvataggio[i] = stringa;
System.out.println("-------");
i++;
System.out.println("Nodo " + i + ":");
System.out.println("scalX:" + scal_X);
System.out.println("scalY:" + scal_Y);
System.out.println("LayoutX:" + Lyt_X);
System.out.println("LayoutY:" + Lyt_Y);
System.out.println("Width:" + width);
System.out.println("Height:" + height);
System.out.println(stringa_nodo);
//save on .txt
}
System.out.println(stringa_salvataggio);
}
});
As I said before, I don't know how to get the radius of Circle to recreate it on loading. Rectangles and squares aren't a problem, I can take all infos I need.
Thanks in advance.

Painting sprite in unity

Problem :
I want to make prototype for cleaning windows (I mean cleaning dirty windows) in unity.
I was searching about this subject and finding that I can change pixel by Texture2D.SetPixel().
I try to do it by this method, First I enabled read/write of texture and try this method but nothing happened on my sprite.
So I want to ask it if it's possible to change alpha of the sprite that is clicked by mouse or touched to show the below sprite of original one !?
My Code :
private RaycastHit2D hitInfo;
private SpriteRenderer spriteRendererComponent;
private Color zeroAlpha;
// Use this for initialization
void Start ()
{
spriteRendererComponent = transform.GetComponent<SpriteRenderer>();
zeroAlpha = Color.blue;
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0))
{
MouseClick();
}
}
public void MouseClick()
{
Vector2 mousePosition = Vector2.zero;
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
hitInfo = Physics2D.Raycast(mousePosition, Vector2.zero);
if (hitInfo)
{
spriteRendererComponent.sprite.texture.SetPixel((int)hitInfo.point.x, (int)hitInfo.point.y, zeroAlpha);
spriteRendererComponent.sprite.texture.Apply();
}
}
Answer :
You can use this thread for Optimizing of changing pixels of sprite
I've found answer about changing pixels of sprite (Painting)
public float radius;
public Color InitialColor;
private RaycastHit2D hitInfo;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
if (CustomInput.ControlStay())
{
hitInfo = CustomInput.ClickednTouched().hitInfo;
if (hitInfo)
{
UpdateTexture();
}
}
}
public Texture2D CopyTexture2D(Texture2D copiedTexture2D)
{
float differenceX;
float differenceY;
//Create a new Texture2D, which will be the copy
Texture2D texture = new Texture2D(copiedTexture2D.width, copiedTexture2D.height);
//Choose your filtermode and wrapmode
texture.filterMode = FilterMode.Bilinear;
texture.wrapMode = TextureWrapMode.Clamp;
//Center of hit point circle
int m1 = (int)((hitInfo.point.x + 2.5f) / 5 * copiedTexture2D.width);
int m2 = (int)((hitInfo.point.y + 2.5f) / 5 * copiedTexture2D.height);
for (int x = 0; x < texture.width; x++)
{
for (int y = 0; y < texture.height; y++)
{
differenceX = x - m1;
differenceY = y - m2;
//INSERT YOUR LOGIC HERE
if (differenceX * differenceX + differenceY * differenceY <= radius * radius)
{
//This line of code and if statement, turn all texture pixels within radius to zero alpha
texture.SetPixel(x, y, InitialColor);
}
else
{
//This line of code is REQUIRED. Do NOT delete it. This is what copies the image as it was, without any change
texture.SetPixel(x, y, copiedTexture2D.GetPixel(x, y));
}
}
}
//This finalizes it. If you want to edit it still, do it before you finish with Apply(). Do NOT expect to edit the image after you have applied.
texture.Apply();
return texture;
}
public void UpdateTexture()
{
SpriteRenderer mySpriteRenderer = gameObject.GetComponent<SpriteRenderer>();
Texture2D newTexture2D = CopyTexture2D(mySpriteRenderer.sprite.texture);
//Get the name of the old sprite
string tempName = mySpriteRenderer.sprite.name;
//Create a new sprite
mySpriteRenderer.sprite = Sprite.Create(newTexture2D, mySpriteRenderer.sprite.rect, new Vector2(0.5f, 0.5f));
//Name the sprite, the old name
mySpriteRenderer.sprite.name = tempName;
//Update the material
//If you have multiple sprites, you will want to do this in a loop
//mySpriteRenderer.material.mainTexture = newTexture2D;
//mySpriteRenderer.material.shader = Shader.Find("Unlit/Transparent");
}
Another problem :
Finding pixel on sprite :
In Unity3d we have RaycastHit.textureCoord but it doesn't exist anymore in 2D. I was searching about this problem, a lot but I didn't find anything useful.
So I want to know the solution for this problem and I'm wondering why method like textureCoord in 3D doesn't exist in 2D.
Answer :
I've found answer again as you see in the previous code for finding pixel on sprite.
Thread : Finding pixel on sprite in Unity
Check this out!
I have fixed your script. Works on different texture sizes. Different texture places and camera size. Require box collider 2d.
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
public float radius;
public Color InitialColor;
private RaycastHit2D hitInfo;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
hitInfo = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if (hitInfo)
{
UpdateTexture();
}
}
if (Input.GetMouseButtonUp(0))
{
Resources.UnloadUnusedAssets();
}
}
public Texture2D CopyTexture2D(Texture2D copiedTexture2D)
{
float differenceX;
float differenceY;
//Create a new Texture2D, which will be the copy
Texture2D texture = new Texture2D(copiedTexture2D.width, copiedTexture2D.height);
//Choose your filtermode and wrapmode
texture.filterMode = FilterMode.Bilinear;
texture.wrapMode = TextureWrapMode.Clamp;
//Center of hit point circle
int m1 = (int)((hitInfo.point.x - hitInfo.collider.bounds.min.x) * (copiedTexture2D.width / hitInfo.collider.bounds.size.x));
int m2 = (int)((hitInfo.point.y - hitInfo.collider.bounds.min.y) * (copiedTexture2D.height / hitInfo.collider.bounds.size.y));
//Vector2 extremeScreenPoint = Camera.main.ScreenToWorldPoint(new Vector2(0, 0));
//Debug.Log("extremeScreenPoint= " + extremeScreenPoint.x
// + " hitInfo.point.x =" + hitInfo.point.x
// //+ " mousePosition =" + Camera.main.ScreenToWorldPoint(Input.mousePosition).x
// + " bounds.min =" + hitInfo.collider.bounds.min .x
// + " bounds.max =" + hitInfo.collider.bounds.max .x
// + " size =" + hitInfo.collider.bounds.size.x
// + " hit =" + (hitInfo.point.x - hitInfo.collider.bounds.min.x)
// + " pixels =" + (hitInfo.point.x - hitInfo.collider.bounds.min.x) * (copiedTexture2D.width / hitInfo.collider.bounds.size.x)
// );
for (int x = 0; x < texture.width; x++)
{
for (int y = 0; y < texture.height; y++)
{
differenceX = x - m1;
differenceY = y - m2;
//INSERT YOUR LOGIC HERE
if (differenceX * differenceX + differenceY * differenceY <= radius * radius)
{
//This line of code and if statement, turn all texture pixels within radius to zero alpha
texture.SetPixel(x, y, InitialColor);
}
else
{
//This line of code is REQUIRED. Do NOT delete it. This is what copies the image as it was, without any change
texture.SetPixel(x, y, copiedTexture2D.GetPixel(x, y));
}
}
}
//This finalizes it. If you want to edit it still, do it before you finish with Apply(). Do NOT expect to edit the image after you have applied.
texture.Apply();
//DestroyImmediate(copiedTexture2D, true);
return texture;
}
public void UpdateTexture()
{
SpriteRenderer mySpriteRenderer = gameObject.GetComponent<SpriteRenderer>();
Texture2D newTexture2D = CopyTexture2D(mySpriteRenderer.sprite.texture);
//Get the name of the old sprite
string tempName = mySpriteRenderer.sprite.name;
//Create a new sprite
mySpriteRenderer.sprite = Sprite.Create(newTexture2D, mySpriteRenderer.sprite.rect, new Vector2(0.5f, 0.5f));
//Name the sprite, the old name
mySpriteRenderer.sprite.name = tempName;
//Update the material
//If you have multiple sprites, you will want to do this in a loop
//mySpriteRenderer.material.mainTexture = newTexture2D;
//mySpriteRenderer.material.shader = Shader.Find("Unlit/Transparent");
}
}
I worked with Writing and Reading a Texture once (for a scratching card).
You have to consider that when you change the Pixels of the Sprite, you are changing the pixels for the entire texture. So let's say that i change the pixel 1x1, most likely it will not change the pixel 1x1 in my sprite if i have a bunch of sprites in the same texture. So you have to consider the offsets of the sprite and reposition the pixel that you want to change.
Try to do something like this:
public void MouseClick()
{
Vector2 offset = new Vector2(XXX, YYY);
Vector2 mousePosition = Vector2.zero;
mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
hitInfo = Physics2D.Raycast(mousePosition, Vector2.zero);
if (hitInfo)
{
spriteRendererComponent.sprite.texture.SetPixel((int)hitInfo.point.x + offset.x, (int)hitInfo.point.y + offset.y, zeroAlpha);
spriteRendererComponent.sprite.texture.Apply();
}
}