I'm trying to implement a tile-based platforming system for a 2D game demo from this C++ code into C#,
This is what I have written, It doesn't compile though due to the var variable usage.
class GameLogic
{
// Level storage
String sLevel;
int nLevelWidth;
int nLevelHeight;
// Player Properties
float fPlayerPosX = 1.0f;
float fPlayerPosY = 1.0f;
float fPlayerVelX = 0.0f;
float fPlayerVelY = 0.0f;
bool bPlayerOnGround = false;
// Camera properties
float fCameraPosX = 0.0f;
float fCameraPosY = 0.0f;
public GameLogic()
{
var GetTile = (ref int x, ref int y) =>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
return sLevel[y * nLevelWidth + x];
else
return "";
};
var SetTile = (ref int x, ref int y, string c)=>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
sLevel[y * nLevelWidth + x] = c;
};
}
}
What do you suggest I should do here, to implement this functionality. Thank you for your time.
It doesn´t compile because of the var statement.
When you use LambdaExpressions and you want to save it to a variable you have to define the delegate Type.
But you can´t use ref variables here. But that is ok, because you don´t change the value of x or y.
You need a Delegate that describes the type of your function.
You could build you own delegate type but in C# there are two built in types for that case.
Action & Action<T1-16> -> always returns void
Func<T1-17> you can define the return type
The GetTile function takes two int and returns a string.
So you can use Func<int, int, string>.
The SetTitle function has one string parameter more but no return type.
That would be Action<int, int, string>.
internal class GameObject
{
private int nLevelWidth;
private int nLevelHeight;
public GameLogic()
{
Func<int, int, string> GetTile = (int x, int y) =>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
return sLevel[y * nLevelWidth + x];
else
return "";
};
Action<int, int, string> SetTile = (int x, int y, string c)=>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
sLevel[y * nLevelWidth + x] = c;
};
}
}
That´s a good article about delegates.
As other answers have correctly noted:
You can't use var with a lambda in C# because the type is ambiguous. You have to state the delegate type explicitly.
Your use of ref indicates that you have misunderstood how outer variable capture works in both C++ and C#. You don't need to make these ref, and the outer variables will be captured automatically.
However none of the answers given so far have told you the better solution to this problem, which is to not use lambdas at all! Just use a local function.
public GameLogic()
{
... other code here ...
string GetTile(int x, int y)
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
return sLevel[y * nLevelWidth + x];
else
return "";
}
No lambdas required.
Note that this solution requires C# 7.0 or higher.
(An even better solution would be to put all this code into a class; make a custom data type that does this work for you. But you asked specifically about how to port this code directly, not how to refactor it.)
Lambda Expressions doesn't have a compile time type, but is convertible to any matching delegate or expression type. That's why according to MSDN lambda expressions can't be implicitly typed (also check this answer by Eric Lippert
So:
Func<int, int, string> GetTile = (int x, int y) =>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
return sLevel[y * nLevelWidth + x];
else
return "";
};
Action<int, int, string> SetTile = (int x, int y, string c)=>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
sLevel[y * nLevelWidth + x] = c;
};
You can declare two delegates like below:
delegate void ActionRef(ref int x, ref int y, string c);
delegate string FuncRef(ref int x, ref int y);
And instead of using var, you can these delegates as the type of function.
public GameLogic()
{
FuncRef GetTile = (ref int x, ref int y) =>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
return sLevel[y * nLevelWidth + x];
else
return "";
};
ActionRef SetTile = (ref int x, ref int y, string c) =>
{
if (x >= 0 && x < nLevelWidth && y >= 0 && y < nLevelHeight)
sLevel[y * nLevelWidth + x] = c;
};
}
I'm trying to calculate on a 2D array 8X8 grid all paths and have the algorithm go over all squares on the grid and configure the shortest path.
See code below:
public static int Mileage(int[,] arr, int x, int y, int miles)
{
if (x < 0 || y < 0 || x > 5 || y > 5 || arr[x, y] == 2) return 99;
if (arr[x, y] == 1) return miles;
arr[x, y] = 2;
miles++;
Console.WriteLine(miles);
int want2 = Math.Min(Mileage(arr, x - 1, y, miles), Mileage(arr, x + 1, y, miles));
int want1 = Math.Min(Mileage(arr, x, y - 1, miles), Mileage(arr, x, y + 1, miles));
return Math.Min(want1, want2);
}
Im doing a FastFourier Transform. So the return values are in the range 0-255 with the higher the volume the bigger the value
Now I have different shapes of various colors. Depending on the volume at the same point in the sound file the FFT can return e.g 1 (low volume) or e.g. 155 (high volume)
I need to brighten or (return to original color if 0 returned) the FillColor of the shape depending on the return value (0-255)
So how do I:
a) Scale the return value in accordance to the sound volume (volume 0-100)
b) brighten the color (e.g. Red by the scaled return value)
Note. Its important that the color is brightened if value > 0
An edit for those who think I DEMAND help.
private void _t_Tick(object sender, EventArgs e)
{
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT8192); //get ch.annel fft data
if (ret < -1) return;
int x, y;
int b0 = 0;
//computes the spectrum data, the code is taken from a bass_wasapi sample.
for (x = 0; x < _lines; x++)
{
float peak = 0;
int b1 = (int)Math.Pow(2, x * 10.0 / (_lines - 1));
if (b1 > 1023) b1 = 1023;
if (b1 <= b0) b1 = b0 + 1;
for (; b0 < b1; b0++)
{
if (peak < _fft[1 + b0]) peak = _fft[1 + b0];
}
y = (int)(Math.Sqrt(peak) * 3 * 255 - 4);
if (y > 255) y = 255;
if (y < 0) y = 0;
_spectrumdata.Add((byte)y);
//Console.Write("{0, 3} ", y);
}
if (DisplayEnable) _spectrum.Set(_spectrumdata);
for (int i = 0; i < _spectrumdata.ToArray().Length; i++)
{
try
{
//if (_spectrumdata[i] > mth)
//{
// _shapes.ToArray()[i].FillColor = _colors.ToArray()[i];// Class1.Increase(_colors.ToArray()[i], _spectrumdata[i]);
//}
//else
//{
// _shapes.ToArray()[i].FillColor = Color.Black; //Class1.Increase(Color.Black, _spectrumdata[i]);
//}
//double d = Math.Round(((float)_spectrumdata[i]) / 255 , 2);
double[] d = GetScaling(_spectrumdata.ToArray(), 0,1);
if (_spectrumdata[i] > mth)
{
_shapes.ToArray()[i].FillColor = ControlPaint.Light(_colors.ToArray()[i], Convert.ToSingle(d[i]));
}
else
{
_shapes.ToArray()[i].FillColor = _colors.ToArray()[i]; ;// Color.Black; //Class1.Increase(Color.Black, _spectrumdata[i]);
}
}
catch (Exception)
{
}
try
{
_chart.Series["wave"].Points.RemoveAt(0);
}
catch (Exception)
{
}
}
_spectrumdata.Clear();
int level = BassWasapi.BASS_WASAPI_GetLevel();
_l.Value = (Utils.LowWord32(level));
_r.Value = (Utils.HighWord32(level));
if (level == _lastlevel && level != 0) _hanctr++;
_lastlevel = level;
//Required, because some programs hang the output. If the output hangs for a 75ms
//this piece of code re initializes the output so it doesn't make a gliched sound for long.
if (_hanctr > 3)
{
_hanctr = 0;
_l.Value = (0);
_r.Value = (0);
Free();
Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
_initialized = false;
Enable = true;
}
}
I have absolutely no clue what the hell you're talking about, but if you're just trying to take a number 1-155 and amplify it to 1-255, try multiplying.
Some simple math (155x = 255) has revealed that x, the number you have to multiply by, is equal to 51/31 or ~1.64516129.
So, what you would do is multiply your original brightness, lets say 35, by 1.64516129.
35 * 1.64516129 = 57.58064.
You can use the Math class to round the number to the nearest integer, which you can then use as your new brightness value.
I'm trying to randomly generate blocks on a flat map and make it so that they don't overlap each other.
I have made a matrix (c# array) of the size of the map (500x500), the blocks have a scale between 1 and 5.
The code works but if a generated block overlaps another one, it is destroyed and not regenerated somewhere else.
Only around 80 of the 1000 blocks I try to generate don't overlap another block.
Here is a picture of the map with around 80 blocks generated, the green squares are blocks
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
bool elementFound = false;
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el;
// Randomly generate block size and position
int size = Random.Range(minScale, maxScale + 1);
int x = Random.Range(0, mapSizex + 1 - size);
int y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
if (elementFound)
continue;
else {
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
}
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
}
}
I thought of 3 possible fixes
I should set the size of the block depending of the place it has.
I should use another randomization algorithm.
I'm not doing this right.
What do you think is the best idea ?
UPDATE
I got the code working much better. I now try to instantiate the blocks multiple times if needed (maximum 5 for the moment) and I fixed the bugs. If there are already many elements on the map, they will not always be instantiated and that's what I wanted, I just have to find the right amount of times it will try to instantiate the block.
I tried instantiating 1280 elements on a 500x500 map. It takes only about 1.5 second and it instantiated 1278/1280 blocks (99.843%).
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
bool elementFound = false;
int cnt = 0;
// Generate every block
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el = null;
// Randomly generate block size and position
int size, x, y, tryCnt = 0;
// Try maximum 5 times to generate the block
do {
elementFound = false;
// Randomly set block size and position
size = Random.Range(minScale, maxScale + 1);
x = Random.Range(0, mapSizex + 1 - size);
y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
tryCnt++;
} while (elementFound && tryCnt < 5);
if (tryCnt >= 5 && elementFound) continue;
// Instantiate the block
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
cnt++;
}
print("Instantiated " + cnt + "/" + ratio * generationDefault);
}
This is incredibly difficult to do well.
Here's a quick solution you'll maybe like ... depending on your scene.
actualWidth = 500 //or whatever. assume here is square
// your blocks are up to 5 size
chunkWidth = actualWidth / 5
// it goes without saying, everything here is an int
kChunks = chunkWidth*chunkWidth
List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();
howManyWanted = 1000
shuf = shuf.Take(howManyWanted)
foreach( i in shuf )
x = i % actualWidth
y = i / actualWidth
make block at x y
put block in list allBlocks
HOWEVER ............
...... you'll see that this looks kind of "regular", so do this:
Just randomly perturb all the blocks. Remember, video game programming is about clever tricks!
Ideally, you have to start from the middle and work your way out; in any event you can't just do them in a line. Shuffling is OK. So, do this ..
harmonic = 3 //for example. TRY DIFFERENT VALUES
function rh = Random.Range(1,harmonic) (that's 1 not 0)
function rhPosNeg
n = rh
n = either +n or -n
return n
function onePerturbation
{
allBlocks = allBlocks.OrderBy(r => Random.value) //essential
foreach b in allBlocks
newPotentialPosition = Vector2(rhPosNeg,rhPosNeg)
possible = your function to check if it is possible
to have a block at newPotentialPosition,
however be careful not to check "yourself"
if possible, move block to newPotentialPosition
}
The simplest approach is just run onePerturbation, say, three times. Have a look at it between each run. Also try different values of the harmonic tuning factor.
There are many ways to perturb fields of differently-sized blocks, above is a KISS solution that hopefully looks good for your situation.
Coding note...
How to get sets of unique random numbers.
Just to explain this line of code...
List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();
If you are new to coding: say you want to do this: "get a hundred random numbers, from 1 to million, but with no repeats".
Fortunately, this is a very well known problem with a very simple solution.
The way you get numbers with no repeats, is simply shuffle all the numbers, and then take how many you want off the top.
For example, say you need a random couple of numbers from 1-10 but with no repeats.
So, here's the numbers 1-10 shuffled: 3,8,6,1,2,7,10,9,4,5
Simply take what you need off the front: so, 3, 8, 6 etc.
So to make an example let's say you want twelve numbers, no repeats, from 1 through 75. So the first problem is, you want a List with all the numbers up to 75, but shuffled. In fact you do that like this ..
List<int> shuf = Enumerable.Range(1,75).OrderBy(r=>Random.value).ToList();
So that list is 75 items long. You can check it by saying foreach(int r in shuf) Debug.Log(r);. Next in the example you only want 12 of those numbers. Fortunately there's a List call that does this:
shuf = shuf.Take(12)
So, that's it - you now have 12 numbers, no repeats, all random between 1 and 75. Again you can check with foreach(int r in shuf) Debug.Log(r);
In short, when you want "n" numbers, no repeats, between 1 and Max, all you have to so is this:
List<int> shuf = Enumerable.Range(1,Max).OrderBy(r=>Random.value).ToList();
shuf = shuf.Take(n);
et voilà, you can check the result with foreach(int r in shuf) Debug.Log(r);
I just explain this at length because the question is often asked "how to get random numbers that are unique". This is an "age-old" programming trick and the answer is simply that you shuffle an array of all the integers involved.
Interestingly, if you google this question ("how to get random numbers that are unique") it's one of those rare occasions where google is not much help, because: whenever this question is asked, you get a plethora of keen new programmers (who have not heard the simple trick to do it properly!!) writing out huge long complicated ideas, leading to further confusion and complication.
So that's how you make random numbers with no repeats, fortunately it is trivial.
if (elementFound) continue; will skip out this current loop iteration. You need to wrap the int x=Random..; int y=Random()..; part in a while loop with the condition being while(/* position x/y already occupued*/) { /* generate new valid point */} like this for example:
void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
for (int i = 0; i < ratio * generationDefault; i++) {
GameObject el;
// Randomly generate block size and position
bool elementFound = false;
int size, x, y;
do
{
elementFound = false;
size = Random.Range(minScale, maxScale + 1);
x = Random.Range(0, mapSizex + 1 - size);
y = Random.Range(0, mapSizey + 1 - size);
// Check if there is already an element
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] != null)
elementFound = true;
} while(elementFound);
el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
el.transform.localScale *= size;
// Create element on map array
for (int j = x; j < x + size; j++)
for (int k = y; k < y + size; k++)
if (map[j][k] == null) {
map[j][k] = el.GetComponent<ObjectInterface>();
}
}
}
You shouldn't be getting that many collisions.
Assuming your blocks were ALL 5 units wide and you're trying to fit them into a grid of 500,500 you would have 100*100 spaces for them at minimum, which gives 10,000 spaces into which to fit 1,000 blocks.
Try playing around with this code:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Program
{
static void Main()
{
var result = PlaceNonOverlappingBlocks(1000, 5, 500, 500);
}
static List<Block> PlaceNonOverlappingBlocks(int count, int maxBlockSize, int mapX, int mapY)
{
var map = new bool[mapY, mapX];
var rng = new Random();
var result = new List<Block>(count);
int collisions = 0;
while (count > 0)
{
int size = rng.Next(1, maxBlockSize + 1);
int x = rng.Next(0, mapX - size);
int y = rng.Next(0, mapY - size);
if (fits(map, x, y, size))
{
result.Add(new Block(x, y, size));
addToMap(map, x, y, size);
--count;
}
else
{
if (++collisions> 100000)
throw new InvalidOperationException("Hell has frozen over");
}
}
// This is just for diagnostics, and can be removed.
Console.WriteLine($"There were {collisions} collisions.");
return result;
}
static void addToMap(bool[,] map, int px, int py, int size)
{
for (int x = px; x < px+size; ++x)
for (int y = py; y < py + size; ++y)
map[y, x] = true;
}
static bool fits(bool[,] map, int px, int py, int size)
{
for (int x = px; x < px + size; ++x)
for (int y = py; y < py + size; ++y)
if (map[y, x])
return false;
return true;
}
internal class Block
{
public int X { get; }
public int Y { get; }
public int Size { get; }
public Block(int x, int y, int size)
{
X = x;
Y = y;
Size = size;
}
}
}
}
I have a list of Points List<Point> newcoor = new List<Point>(); and specific coordinate which is the center of the List of Points. int centerx, centery;
What I want to do is add 1 with centerx and subtract 1 with centery until it reaches a combination that will match a Point inside a list. Then store that point inside an array. This is my code:
List<Point> newcoor = new List<Point>(); // list of points that where the tempx and tempy will be compared to.
//...
Point[] vector = new Point[4];
int x = 0;
while (x <= 3)
{
var tempx = centerx + 1; //add 1 to centerx
var tempy = centerx - 1; //subtrat 1 to centery
int y = 0;
if (y < newcoor.Count() - 1 && newcoor[y].X == tempx && newcoor[y].Y == tempy) // compare if there is a Point in the List that is equal with the (tempx,tempy) coordinate
{
vector[x].X = tempx;// store the coordinates
vector[x].Y = tempy;
}
break; // this is what I don't understand, I want to exit the loop immediately if the if-condition is true. And add 1 to x so the while loop will update.
}
Tried New Code:
for (int y = 0; y < newcoor.Count() - 1; y++)
{
var tempx = centerx + 1;
var tempy = centery - 1;
for (int x = 0; x < newcoor.Count() - 1; x++)
{
if (newcoor[y].X == tempx && newcoor[y].Y == tempy)
{
//vectorPoints.Add(new Point(tempx,tempy));
MessageBox.Show("success");
}
}
}
But no messagebox success shows, meaning there was no match. but there must be.
All I need is 4 output that's why I have conditon while (x <= 3)
Update:
My centerx = 30 and centery = 28
And here is my list:
What I want to do is add 1 to centerx and subtract 1 to centery
from original centerx= 30 and centery= 28, it should be
(31,27)
(32,26)
(33,25)
(34,24)
(35,23) <----- This should be the to the one with the same value inside my list, which is shown in the image above.
No idea what you're hoping for here, but there's several problems I can spot anyway;
Firtly, tempx and tempy will be the same value on each loop, as nothing inside the loop manipulates centerx or centery.
Secondly, the loop will exit on the first run, as the break statement is not inside the if {..} block. Perhaps you meant;
if (y < newcoor.Count() - 1 && newcoor[y].X == tempx && newcoor[y].Y == tempy)
{
vector[x].X = tempx;// store the coordinates
vector[x].Y = tempy;
break; // <-- needs to be inside the if{..} block
}