react-native display image in GLView - reactjs

I'm trying to display an image on a GLView in react native.
I was inspired by this link to write my code https://webglfundamentals.org/webgl/lessons/webgl-2d-drawimage.html
I have this component who calls a GLContext2D class:
import React, { Component } from 'react';
import { Text, View, StyleSheet } from 'react-native';
import Expo from 'expo';
import GLContext2D from '../../lib/GLContext2D';
export default class Scene extends Component {
constructor(props, context) {
super(props, context);
this.state = {
ready: false,
};
}
componentDidMount() {
(async () => {
this._textureAsset = Expo.Asset.fromModule(
require('../../test.jpg')
);
await this._textureAsset.downloadAsync();
console.log("ok");
this.setState({ ready: true });
})();
}
render () {
return this.state.ready
? <Expo.GLView
style={styles.view}
onContextCreate={this._onContextCreate}
/>
: <Expo.AppLoading />;
}
_onContextCreate = gl => {
console.log(this._textureAsset);
var ctx = new GLContext2D(gl);
var test = ctx.createTextureFromAsset(this._textureAsset);
ctx.clear();
gl.clearColor(0, 0, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
ctx.drawImage(test.texture, test.width, test.height, 0, 200);
gl.flush();
gl.endFrameEXP();
}
}
const styles = StyleSheet.create({
view: {
width: 500,
height: 1000,
backgroundColor: 'yellow'
}
});
And the GLContext2D class:
import Expo from 'expo';
import {mat4} from 'gl-matrix';
const vertSrc = `
attribute vec4 a_position;
attribute vec2 a_texcoord;
uniform mat4 u_matrix;
uniform mat4 u_textureMatrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * a_position;
v_texcoord = (u_textureMatrix * vec4(a_texcoord, 0, 1)).xy;
}
`;
const fragSrc = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
if (v_texcoord.x < 0.0 ||
v_texcoord.y < 0.0 ||
v_texcoord.x > 1.0 ||
v_texcoord.y > 1.0) {
gl_FragColor = vec4(1, 0, 1, 1); // blue
return;
}
gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;
export default class GLContext2D {
constructor(gl) {
var vert;
var frag;
this._gl = gl;
vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, vertSrc);
gl.compileShader(vert);
frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, fragSrc);
gl.compileShader(frag);
this._program = gl.createProgram();
gl.attachShader(this._program, vert);
gl.attachShader(this._program, frag);
gl.linkProgram(this._program);
this._locations = {
position: gl.getAttribLocation(this._program, "a_position"),
texcoord: gl.getAttribLocation(this._program, "a_texcoord"),
matrix: gl.getUniformLocation(this._program, "u_matrix"),
textureMatrix: gl.getUniformLocation(this._program, "u_textureMatrix"),
texture: gl.getUniformLocation(this._program, "u_texture")
};
this._createBuffers();
}
_createBuffers() {
var positions;
var texcoords;
var gl;
gl = this._gl;
this._positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
positions = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
this._texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this._texcoordBuffer);
texcoords = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
}
async createTextureFromAsset(asset) {
var gl;
var tex;
var texData;
var img;
gl = this._gl;
tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
texData = {
width: 1,
height: 1,
texture: tex,
};
texData.width = asset.width;
texData.height = asset.height;
gl.bindTexture(gl.TEXTURE_2D, texData.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, asset);
return (texData);
}
//ctx.drawImage(tex, texWidth, texHeight, dstX, dstY);
//ctx.drawImage(tex, texWidth, texHeight, dstX, dstY, dstWidth, dstHeight)
drawImage(
tex, texWidth, texHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight, srcRotation) {
var gl;
var matrix, matrix2;
var texMatrix;
if (dstX === undefined) {
dstX = srcX;
srcX = 0;
}
if (dstY === undefined) {
dstY = srcY;
srcY = 0;
}
if (srcWidth === undefined) {
srcWidth = texWidth;
}
if (srcHeight === undefined) {
srcHeight = texHeight;
}
if (dstWidth === undefined) {
dstWidth = srcWidth;
srcWidth = texWidth;
}
if (dstHeight === undefined) {
dstHeight = srcHeight;
srcHeight = texHeight;
}
if (srcRotation === undefined) {
srcRotation = 0;
}
gl = this._gl;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.useProgram(this._program);
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
gl.enableVertexAttribArray(this._locations.position);
gl.vertexAttribPointer(this._locations.position, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this._texcoordBuffer);
gl.enableVertexAttribArray(this._locations.texcoord);
gl.vertexAttribPointer(this._locations.texcoord, 2, gl.FLOAT, false, 0, 0);
matrix = mat4.create();
matrix2 = mat4.create();
mat4.ortho(matrix, 0, 1000, 1000, 0, -1, 1);
mat4.translate(matrix2, matrix, [dstX, dstY, 0]);
mat4.copy(matrix, matrix2);
mat4.scale(matrix2, matrix, [dstWidth, dstHeight, 1]);
mat4.copy(matrix, matrix2);
gl.drawingBufferHeight = 320;
gl.uniformMatrix4fv(this._locations.matrix, false, matrix);
texMatrix = mat4.create();
mat4.fromScaling(texMatrix, [1 / texWidth, 1 / texHeight, 1]);
mat4.translate(matrix2, texMatrix, [texWidth * 0.5, texHeight * 0.5, 0]);
mat4.copy(texMatrix, matrix2);
mat4.rotateZ(matrix2, texMatrix, srcRotation);
mat4.copy(texMatrix, matrix2);
mat4.translate(matrix2, texMatrix, [texWidth * -0.5, texHeight * -0.5, 0]);
mat4.translate(texMatrix, matrix2, [srcX, srcY, 0]);
mat4.scale(matrix2, texMatrix, [srcWidth, srcHeight, 1]);
mat4.copy(texMatrix, matrix2);
gl.uniformMatrix4fv(this._locations.textureMatrix, false, texMatrix);
gl.uniform1i(this._locations.texture, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
clear() {
var gl;
gl = this._gl;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
}
}
I have a blue screen with the clear function but nothing appears from drawImage function.

First off, please learn how to make a Minimal, Complete, and Verifiable example. Emphasis on minimal.
Looking at your code in drawImage you have the size of the canvas hard coded
mat4.ortho(matrix, 0, 1000, 1000, 0, -1, 1);
It seems like you'd want
mat4.ortho(matrix, 0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
Also I have no idea what this line is for
gl.drawingBufferHeight = 320;
Finally I notice you have createTextureFromAsset as an async function but you'd not calling it with an await
const vertSrc = `
attribute vec4 a_position;
attribute vec2 a_texcoord;
uniform mat4 u_matrix;
uniform mat4 u_textureMatrix;
varying vec2 v_texcoord;
void main() {
gl_Position = u_matrix * a_position;
v_texcoord = (u_textureMatrix * vec4(a_texcoord, 0, 1)).xy;
}
`;
const fragSrc = `
precision mediump float;
varying vec2 v_texcoord;
uniform sampler2D u_texture;
void main() {
if (v_texcoord.x < 0.0 ||
v_texcoord.y < 0.0 ||
v_texcoord.x > 1.0 ||
v_texcoord.y > 1.0) {
gl_FragColor = vec4(1, 0, 1, 1); // blue
return;
}
gl_FragColor = texture2D(u_texture, v_texcoord);
}
`;
class GLContext2D {
constructor(gl) {
var vert;
var frag;
this._gl = gl;
vert = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vert, vertSrc);
gl.compileShader(vert);
frag = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(frag, fragSrc);
gl.compileShader(frag);
this._program = gl.createProgram();
gl.attachShader(this._program, vert);
gl.attachShader(this._program, frag);
gl.linkProgram(this._program);
this._locations = {
position: gl.getAttribLocation(this._program, "a_position"),
texcoord: gl.getAttribLocation(this._program, "a_texcoord"),
matrix: gl.getUniformLocation(this._program, "u_matrix"),
textureMatrix: gl.getUniformLocation(this._program, "u_textureMatrix"),
texture: gl.getUniformLocation(this._program, "u_texture")
};
this._createBuffers();
}
_createBuffers() {
var positions;
var texcoords;
var gl;
gl = this._gl;
this._positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
positions = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
this._texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this._texcoordBuffer);
texcoords = [0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texcoords), gl.STATIC_DRAW);
}
async createTextureFromAsset(asset) {
var gl;
var tex;
var texData;
var img;
gl = this._gl;
tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
texData = {
width: 1,
height: 1,
texture: tex,
};
texData.width = asset.width;
texData.height = asset.height;
gl.bindTexture(gl.TEXTURE_2D, texData.texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, asset);
return (texData);
}
//ctx.drawImage(tex, texWidth, texHeight, dstX, dstY);
//ctx.drawImage(tex, texWidth, texHeight, dstX, dstY, dstWidth, dstHeight)
drawImage(
tex, texWidth, texHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight, srcRotation) {
var gl;
var matrix, matrix2;
var texMatrix;
if (dstX === undefined) {
dstX = srcX;
srcX = 0;
}
if (dstY === undefined) {
dstY = srcY;
srcY = 0;
}
if (srcWidth === undefined) {
srcWidth = texWidth;
}
if (srcHeight === undefined) {
srcHeight = texHeight;
}
if (dstWidth === undefined) {
dstWidth = srcWidth;
srcWidth = texWidth;
}
if (dstHeight === undefined) {
dstHeight = srcHeight;
srcHeight = texHeight;
}
if (srcRotation === undefined) {
srcRotation = 0;
}
gl = this._gl;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.useProgram(this._program);
gl.bindBuffer(gl.ARRAY_BUFFER, this._positionBuffer);
gl.enableVertexAttribArray(this._locations.position);
gl.vertexAttribPointer(this._locations.position, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, this._texcoordBuffer);
gl.enableVertexAttribArray(this._locations.texcoord);
gl.vertexAttribPointer(this._locations.texcoord, 2, gl.FLOAT, false, 0, 0);
matrix = mat4.create();
matrix2 = mat4.create();
mat4.ortho(matrix, 0, gl.canvas.width, gl.canvas.height, 0, -1, 1);
mat4.translate(matrix2, matrix, [dstX, dstY, 0]);
mat4.copy(matrix, matrix2);
mat4.scale(matrix2, matrix, [dstWidth, dstHeight, 1]);
mat4.copy(matrix, matrix2);
// gl.drawingBufferHeight = 320;
gl.uniformMatrix4fv(this._locations.matrix, false, matrix);
texMatrix = mat4.create();
mat4.fromScaling(texMatrix, [1 / texWidth, 1 / texHeight, 1]);
mat4.translate(matrix2, texMatrix, [texWidth * 0.5, texHeight * 0.5, 0]);
mat4.copy(texMatrix, matrix2);
mat4.rotateZ(matrix2, texMatrix, srcRotation);
mat4.copy(texMatrix, matrix2);
mat4.translate(matrix2, texMatrix, [texWidth * -0.5, texHeight * -0.5, 0]);
mat4.translate(texMatrix, matrix2, [srcX, srcY, 0]);
mat4.scale(matrix2, texMatrix, [srcWidth, srcHeight, 1]);
mat4.copy(texMatrix, matrix2);
gl.uniformMatrix4fv(this._locations.textureMatrix, false, texMatrix);
gl.uniform1i(this._locations.texture, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
}
clear() {
var gl;
gl = this._gl;
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
}
}
function loadImageP(url) {
return new Promise((fulfill, reject) => {
const img = new Image();
img.crossOrigin = "";
img.onload = fulfill;
img.onerror = reject;
img.src = url;
});
}
const gl = document.querySelector("canvas").getContext("webgl");
const gl2D = new GLContext2D(gl);
async function load(url) {
const e = await loadImageP(url);
const tex = await gl2D.createTextureFromAsset(e.target);
gl2D.drawImage(
tex.texture, tex.width, tex.height,
0, 0, 300, 150);
};
(async function() {
await load("http://i.imgur.com/iFom4eT.png");
}());
canvas { border: 1px solid black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"></script>
<canvas></canvas>

Related

SDL2 Camera Cutting Off Screen Edges

I'm trying to create a simple camera for my SDL2 platformer with C. However, whenever the player reaches the screen edges, it seems to be cut off by the background. Here is what the player looks like normally:
And here is what the player looks like when it reaches the screen edges:
To make the camera follow the player, I'm just creating a SDL_Rect called camera, setting it to the player x and y positions, and setting the viewport with SDL_RenderSetViewport to the camera rectangle.
Here's the code for that:
void handle_camera() {
SDL_Rect camera = {
.x = WIDTH/2 - player.x - BLOCK_SIZE/2,
.y = HEIGHT/2 - player.y - BLOCK_SIZE/2,
.w = WIDTH,
.h = HEIGHT
};
SDL_RenderSetViewport(game.renderer, &camera);
}
Therefore, I was wondering: what's wrong with my camera function and why is the player being cut off when it gets near the screen edges?
Here is the full code if needed (I organized in functions so I hope it's not too hard to understand):
#include <stdio.h>
#include <stdbool.h>
#include <SDL2/SDL.h>
#define WIDTH 1200
#define HEIGHT 800
#define BLOCK_SIZE 50
#define PLATFORM_AMOUNT 11 //This makes sure there are enough iterations of the loop, and also allocates enough memory for the 2D array of platforms.
#define LAVA_AMOUNT 2
#define TRAMPOLINE_AMOUNT 1
//Prototyping Functions
int initialize();
void handle_input();
void draw_player();
void player_moveX();
void player_moveY();
void checkCollisionsX();
void checkCollisionsY();
int rectCollide();
void drawLevel();
void resetPlayer();
void handle_camera();
typedef struct {
SDL_Renderer *renderer;
SDL_Window *window;
SDL_Surface *surface;
bool running;
int FPS;
bool close_requested;
int input[256];
} Game;
Game game = {
.running = true,
.FPS = 80,
.close_requested = false,
.input = {},
};
typedef struct {
int x;
int y;
double x_vel;
double y_vel;
double x_acc;
double y_acc;
int width;
int height;
double accSpeed;
int maxVel;
double gravity;
double friction;
double jumpForce;
double canJump;
} Player;
Player player = {
.y = HEIGHT-(BLOCK_SIZE*2),
.x = BLOCK_SIZE,
.x_vel = 0,
.y_vel = 0,
.x_acc = 0,
.y_acc = 0,
.width = BLOCK_SIZE,
.height = BLOCK_SIZE,
.accSpeed = 0.15,
.maxVel = 7,
.gravity = 0.5,
.friction = 0.15,
.jumpForce = 15,
.canJump = true,
};
int platforms[PLATFORM_AMOUNT][4] = {
{0, 0, BLOCK_SIZE, HEIGHT}, //WALLS
{0, HEIGHT-BLOCK_SIZE, WIDTH, BLOCK_SIZE},
{400-BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE}, //RAMP TO LAVA
{400-BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*3), BLOCK_SIZE, BLOCK_SIZE},
{300, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{800, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{800, HEIGHT-(BLOCK_SIZE*3), BLOCK_SIZE, BLOCK_SIZE},
{800+BLOCK_SIZE, HEIGHT-(BLOCK_SIZE*2), BLOCK_SIZE, BLOCK_SIZE},
{WIDTH-BLOCK_SIZE*10, HEIGHT-(BLOCK_SIZE*8), BLOCK_SIZE, BLOCK_SIZE}, //Blocks above lava
{WIDTH-BLOCK_SIZE*8, HEIGHT-(BLOCK_SIZE*10), BLOCK_SIZE, BLOCK_SIZE},
{BLOCK_SIZE, BLOCK_SIZE*3, BLOCK_SIZE*9, BLOCK_SIZE}, //Top platform
};
int lava[LAVA_AMOUNT][4] = {
{400, HEIGHT-(BLOCK_SIZE*3), 400, BLOCK_SIZE*2},
{BLOCK_SIZE*4, BLOCK_SIZE*2, BLOCK_SIZE*3, BLOCK_SIZE},
};
int trampoline[TRAMPOLINE_AMOUNT][4] = {
{WIDTH/2-(BLOCK_SIZE/2), HEIGHT-(BLOCK_SIZE*5), BLOCK_SIZE, BLOCK_SIZE}
};
int main() {
initialize();
while(game.running && !game.close_requested) { //Game loop
SDL_SetRenderDrawColor(game.renderer, 181, 247, 255, 255);
SDL_RenderClear(game.renderer);
handle_input();
handle_camera();
//Collisions only work in this order: playerMoveX, checkCollisionsX, playerMoveY, checkCollisionsY. Then you can draw the platforms and the player.
player_moveX();
checkCollisionsX();
player_moveY();
checkCollisionsY();
drawLevel();
draw_player();
SDL_RenderPresent(game.renderer);
SDL_Delay(1000/game.FPS);
} //End of game loop
SDL_DestroyRenderer(game.renderer);
SDL_DestroyWindow(game.window);
SDL_Quit();
return 0;
}
int initialize() {
if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) != 0) { //return 0 on success
printf("error initializing SDL: %s\n", SDL_GetError());
return 1;
}
game.window = SDL_CreateWindow("Sam's Platformer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE); //creates window
if (!game.window) {
printf("error creating window: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
Uint32 render_flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC; //creates a renderer
game.renderer = SDL_CreateRenderer(game.window, -1, render_flags);
if (!game.renderer) {
printf("error creating renderer: %s\n", SDL_GetError());
SDL_DestroyWindow(game.window);
SDL_Quit();
return 1;
}
return 0;
}
void handle_input() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
game.close_requested = true;
game.running = false;
}
//printf("input: %p code: %i\n", game.input, event.key.keysym.scancode);
if (event.type == SDL_KEYDOWN) {
game.input[event.key.keysym.scancode] = true;
//printf("True");
}
if (event.type == SDL_KEYUP) {
game.input[event.key.keysym.scancode] = false;
//printf("False");
}
}
if (game.input[SDL_SCANCODE_R]) {
resetPlayer();
}
}
void draw_player() {
SDL_Rect playerRect = {
.x = player.x,
.y = player.y,
.w = player.width,
.h = player.height
};
SDL_SetRenderDrawColor(game.renderer, 0, 200, 50, 255);
SDL_RenderFillRect(game.renderer, &playerRect);
}
void resetPlayer() {
player.y = HEIGHT-(BLOCK_SIZE*2);
player.x = BLOCK_SIZE;
player.x_vel = 0;
player.y_vel = 0;
}
void player_moveX() {
if (game.input[SDL_SCANCODE_LEFT] && player.x_vel > -player.maxVel) {
player.x_acc = -player.accSpeed;
} else if (game.input[SDL_SCANCODE_RIGHT] && player.x_vel < player.maxVel) {
player.x_acc = player.accSpeed;
} else if (abs(player.x_vel) > 0.2) {
if (player.x_vel < 0) {
player.x_acc = player.friction;
} else {
player.x_acc = -player.friction;
}
} else {
player.x_vel = 0;
player.x_acc = 0;
}
player.x_vel += player.x_acc;
player.x += player.x_vel;
}
void player_moveY() {
if (game.input[SDL_SCANCODE_UP] && player.y_vel == 0 && player.y_acc == 0 && player.canJump) {
player.canJump = false;
player.y_vel = -player.jumpForce;
}
player.y_acc += player.gravity;
player.y_vel += player.y_acc;
player.y += player.y_vel;
player.y_acc = 0;
}
void checkCollisionsX() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3])) {
if (player.x_vel < 0) { // If the player moved left and collided with the right side of block
player.x = platforms[i][0] + platforms[i][2];
} else { // If the player moved right and collided with the left side of block
player.x = platforms[i][0] - player.width;
}
player.x_vel = 0;
}
}
/*if (player.x >= WIDTH - player.width) {
player.x = WIDTH - player.width;
player.x_vel = 0;
}*/
for (int i = 0; i < LAVA_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, lava[i][0], lava[i][1], lava[i][2], lava[i][3])) {
resetPlayer();
}
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3])) {
if (player.x_vel < 0) { // If the player moved left and collided with the right side of block
player.x = trampoline[i][0] + trampoline[i][2];
} else { // If the player moved right and collided with the left side of block
player.x = trampoline[i][0] - player.width;
}
player.x_vel = 0;
}
}
}
void checkCollisionsY() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
if (rectCollide(player.x, player.y, player.width, player.height, platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3])) {
if (player.y_vel < 0) { // If the player hit their head
player.y = platforms[i][1] + platforms[i][3];
player.y_vel *= -0.5; // Not -1 because collisions are not perfectly elastic
} else {
player.y = platforms[i][1] - player.height;
player.y_vel = 0;
player.y_acc = 0;
player.canJump = true;
}
}
if (player.y >= HEIGHT - player.height) {
player.y_vel = 0;
player.y = HEIGHT - player.height;
if (!game.input[SDL_SCANCODE_UP]) {
player.canJump = true;
}
}
}
for (int i = 0; i < LAVA_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, lava[i][0], lava[i][1], lava[i][2], lava[i][3])) {
resetPlayer();
}
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
if (rectCollide(player.x, player.y, BLOCK_SIZE, BLOCK_SIZE, trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3])) {
if (player.y_vel < 0) { // If the player hit their head
player.y = trampoline[i][1] + trampoline[i][3];
player.y_vel *= -0.5; // Not -1 because collisions are not perfectly elastic
} else {
player.y = trampoline[i][1] - trampoline[i][3];
player.y_vel = -player.y_vel;
}
}
}
}
int rectCollide(int x1, int y1, int w1, int h1, int x2,int y2, int w2, int h2) {
return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
void drawLevel() {
for (int i = 0; i < PLATFORM_AMOUNT; i++) {
SDL_Rect platform = {platforms[i][0], platforms[i][1], platforms[i][2], platforms[i][3]};
SDL_SetRenderDrawColor(game.renderer, 156, 104, 0, 255);
SDL_RenderFillRect(game.renderer, &platform);
}
for (int i = 0; i < LAVA_AMOUNT; i++) {
int lavaRedColor = 255;
SDL_Rect lavaBlock = {lava[i][0], lava[i][1], lava[i][2], lava[i][3]};
SDL_SetRenderDrawColor(game.renderer, lavaRedColor, 0, 0, 255);
SDL_RenderFillRect(game.renderer, &lavaBlock);
}
for (int i = 0; i < TRAMPOLINE_AMOUNT; i++) {
SDL_Rect trampolineBlock = {trampoline[i][0], trampoline[i][1], trampoline[i][2], trampoline[i][3]};
SDL_SetRenderDrawColor(game.renderer, 235, 52, 229, 255);
SDL_RenderFillRect(game.renderer, &trampolineBlock);
}
}
void handle_camera() {
SDL_Rect camera = {
.x = WIDTH/2 - player.x - BLOCK_SIZE/2,
.y = HEIGHT/2 - player.y - BLOCK_SIZE/2,
.w = WIDTH, //Screen width
.h = HEIGHT //Screen height
};
SDL_RenderSetViewport(game.renderer, &camera);
}
Thanks for any help.
https://gamedev.stackexchange.com/questions/121421/how-to-use-the-sdl-viewport-properly
See this related post. Essentially changing the SDL viewport is not the way you'd typically handle an in game camera. You need to consider drawing your in game entities (level objects etc.) relative to the camera.

2D Raylib Texture Collisions

I was wondering how to properly handle collisions in Raylib. After setting up the basics, like a window, I would first load in the player texture, like so:
Texture2D playerTexture = LoadTexture("Player.png");
After that, I would draw the texture, with the Raylib function DrawTextureEx which allows me to adjust the rotation and scale of the texture. Then, I would add a simple movement script for the player, and that would be pretty much it.
As for collisions, I would just be testing the player collision on a rectangle. To get the area for the rectangle, I would just create a simple Rectangle struct like this:
Rectangle rect1 = {100, 100, 50, 50};
As for getting the player area, I would make something similar:
Rectangle playerArea = {player.x, player.y, playerTexture.width*player.scale, playerTexture.height*player.scale} //Values are stored in Player struct
However, when I try to check the collisons like this, nothing happens:
if (CheckCollisionRecs(playerArea, rect1)) {
DrawText("Collided", 5, 5, 25, BLACK);
}
Here's my full code, if needed:
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#define WIDTH 1200
#define HEIGHT 800
typedef struct {
const int width;
const int height;
int FPS;
} Window;
Window window = {
.FPS = 60,
};
typedef struct {
Vector2 pos;
Vector2 acc;
Vector2 vel;
int width;
int height;
double accSpeed;
int maxVel;
double friction;
double rotation;
double scale;
} Player;
Player player = {
.pos = {WIDTH/2, HEIGHT/2},
.acc = {0, 0},
.vel = {0, 0},
.width = 25,
.height = 25,
.accSpeed = 0.15,
.maxVel = 7,
.friction = 0.2,
.rotation = 0,
.scale = 0.5,
};
void movePlayer();
int rectCollide();
int main() {
InitWindow(WIDTH, HEIGHT, "Window");
SetTargetFPS(window.FPS);
Texture2D playerImg = LoadTexture("Player.png");
Rectangle playerArea = {player.pos.x, player.pos.y, playerImg.width*player.scale, playerImg.height*player.scale};
Rectangle rect1 = {100, 100, 50, 50};
Camera2D camera = { 0 };
camera.target = (Vector2){ player.pos.x + 20.0f, player.pos.y + 20.0f };
camera.offset = (Vector2){WIDTH/2.0f, HEIGHT/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
if (CheckCollisionRecs(playerArea, rect1)) {
DrawText("Collided", 5, 5, 25, BLACK);
}
while (!WindowShouldClose()) {
BeginDrawing();
ClearBackground(SKYBLUE);
movePlayer();
camera.target = (Vector2){ player.pos.x + 20, player.pos.y + 20 };
BeginMode2D(camera);
DrawRectangle(100, 100, 50, 50, BLACK);
DrawTextureEx(playerImg, player.pos, player.rotation, player.scale, RAYWHITE);
EndMode2D();
EndDrawing();
}
UnloadTexture(playerImg);
CloseWindow();
return 0;
}
void movePlayer() {
if (IsKeyDown(KEY_LEFT) && player.vel.x > -player.maxVel) {
player.acc.x = -player.accSpeed;
} else if (IsKeyDown(KEY_RIGHT) && player.vel.x < player.maxVel) {
player.acc.x = player.accSpeed;
} else if (abs(player.vel.x) > 0.2) {
if (player.vel.x < 0) {
player.acc.x = player.friction;
} else {
player.acc.x = -player.friction;
}
} else {
player.vel.x = 0;
player.acc.x = 0;
}
player.vel.x += player.acc.x;
player.pos.x += player.vel.x;
if (IsKeyDown(KEY_UP) && player.vel.y > -player.maxVel) {
player.acc.y = -player.accSpeed;
} else if (IsKeyDown(KEY_DOWN) && player.vel.x < player.maxVel) {
player.acc.y = player.accSpeed;
} else if (abs(player.vel.y) > 0.2) {
if (player.vel.y < 0) {
player.acc.y = player.friction;
} else {
player.acc.y = -player.friction;
}
} else {
player.vel.y = 0;
player.acc.y = 0;
}
player.vel.y += player.acc.y;
player.pos.y += player.vel.y;
}
int rectCollide(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
return x1 + w1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 < y2 + h2;
}
And so, I was wondering: first of all, is there a better way to get the area of the texture, without having to create a Rectangle struct for each texture, and multiply the scale by the texture width and height? Secondly, I was wondering why my collisions aren't being detected. What is wrong with my code?
The collision check
if (CheckCollisionRecs(playerArea, rect1))
precedes the event loop.
Even moving it inside the loop will not have any effect, as playerArea is never updated in your event loop.
A quick fix would be to update the collision box after moving the player.
(example snippet of main, without images)
Rectangle playerArea;
Rectangle rect1 = {100, 100, 50, 50};
while (!WindowShouldClose()) {
movePlayer();
playerArea = (Rectangle) {
player.pos.x,
player.pos.y,
player.width,
player.height,
};
camera.target = (Vector2){ player.pos.x + 20, player.pos.y + 20 };
BeginDrawing();
ClearBackground(SKYBLUE);
BeginMode2D(camera);
DrawRectangleRec(rect1, RED);
DrawRectangle(player.pos.x, player.pos.y, player.width, player.height, RAYWHITE);
EndMode2D();
if (CheckCollisionRecs(playerArea, rect1))
DrawText("Collided", 5, 5, 25, BLACK);
EndDrawing();
}

convert sine-waves package examples to react component

I'm trying to use sine-waves package sine-waves package , in react app, first thing I did is looking for examples, border wave particularly and start to convert it to react component and when I tried to start the app, it gives me this error :
is there a problem in canvas with react
any help will be appreciated ...
this is my attempt:
import React from 'react';
import SineWaves from 'sine-waves';
import _ from 'lodash';
class SineWave extends React.Component {
width = 300;
height = 300;
amplitude = 20;
speed = 1;
ease = 'SineInOut';
waves = [
{
timeModifer: 2,
lineWidth: 1,
amplitude: this.amplitude,
wavelength: 100,
segmentLength: 1,
strokeStyle: 'rgba(255, 255, 255, 0.3333)',
type: 'Sawtooth',
},
{
timeModifer: 2,
lineWidth: 1,
amplitude: -this.amplitude / 2,
wavelength: 100,
segmentLength: 1,
strokeStyle: 'rgba(255, 255, 255, 0.3333)',
type: 'Sawtooth',
},
{
timeModifer: 2,
lineWidth: 1,
amplitude: -this.amplitude,
wavelength: 100,
segmentLength: 1,
strokeStyle: 'rgba(255, 255, 255, 0.3333)',
type: 'Square',
},
];
render() {
const { speed, width, height, ease, waves } = this;
const bottomWaves = new SineWaves({
el: document.getElementById('bottom-waves'),
speed,
width,
height,
ease,
waves: _.clone(waves, true),
rotate: 0,
resizeEvent() {
let gradient = this.ctx.createLinearGradient(0, 0, this.width, 0);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
let index = -1;
let { length } = this.waves;
while (++index < length) {
this.waves[index].strokeStyle = gradient;
}
// Clean Up
index = void 0;
length = void 0;
gradient = void 0;
},
});
const topWaves = new SineWaves({
el: document.getElementById('top-waves'),
speed: -speed,
width,
height,
ease,
waves: _.clone(waves, true),
rotate: 0,
resizeEvent() {
let gradient = this.ctx.createLinearGradient(0, 0, this.width, 0);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
let index = -1;
let { length } = this.waves;
while (++index < length) {
this.waves[index].strokeStyle = gradient;
}
// Clean Up
index = void 0;
length = void 0;
gradient = void 0;
},
});
const leftWaves = new SineWaves({
el: document.getElementById('left-waves'),
speed,
width: height,
height: width,
ease,
waves: _.clone(waves, true),
rotate: 90,
resizeEvent() {
let gradient = this.ctx.createLinearGradient(0, 0, this.width, 0);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
let index = -1;
let { length } = this.waves;
while (++index < length) {
this.waves[index].strokeStyle = gradient;
}
// Clean Up
index = void 0;
length = void 0;
gradient = void 0;
},
});
const rightWaves = new SineWaves({
el: document.getElementById('right-waves'),
speed: -speed,
width: height,
height: width,
ease,
waves: _.clone(waves, true),
rotate: 90,
resizeEvent() {
let gradient = this.ctx.createLinearGradient(0, 0, this.width, 0);
gradient.addColorStop(0, 'rgba(0, 0, 0, 0)');
gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.5)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
let index = -1;
let { length } = this.waves;
while (++index < length) {
this.waves[index].strokeStyle = gradient;
}
// Clean Up
index = void 0;
length = void 0;
gradient = void 0;
},
});
return (
<>
<h1 id="title">
SineWaves.js
</h1>
<h2 id="sub-title">Border Example</h2>
<div id="box">
<h2 id="example-title">Animated Borders</h2>
<canvas className="wave" id="top-waves" />
<canvas className="wave" id="left-waves" />
<canvas className="wave" id="bottom-waves" />
<canvas className="wave" id="right-waves" />
</div>
</>
);
}
}
export default SineWave;
Generally speaking a message like
uncaught : No Canvas Selected would either mean you didn't specify a canvas target at all or it simply can't find it because it doesn't exist.
Looking at the parameters you're passing to the constructor of SineWaves
const rightWaves = new SineWaves({
el: document.getElementById('right-waves'),
you're telling it to look for a <canvas> element with the id right-waves.
Try setting one up like:
<canvas id="right-waves"></canvas>

How to use SwiftUI with different views?

I have SCNCylinder in a SCNScene and a button in a SwiftUI frame below it. Whenever the button is pressed the cylinder is expected to rotate by 90°. I am not getting the expected result with my code.
//SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Button(action: {
// What to perform
let rotationangle = 180.0
}) {
// How the button looks like
Text("90°")
.frame(width: 100, height: 100)
.position(x: 225, y: 500)
}
SceneKitView()
.frame(width: 300, height: 300)
.position(x: 0, y: 200)
}
}
}
//SceneKit
struct SceneKitView: UIViewRepresentable {
func makeUIView(context:
UIViewRepresentableContext<SceneKitView>) -> SCNView {
//Scene properties
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
//Cylinder properties
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
//Pivot and rotation of the cylinder
func degreesToRadians(_ degrees: Float) -> CGFloat {
return CGFloat(degrees * .pi / 180)
}
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
//Line with an error: Use of unresolved identifier ‘rotationangle’
let rotation = SCNAction.rotate(by: degreesToRadians(rotationangle), around: SCNVector3 (1, 0, 0), duration: 5)
sceneView.scene?.rootNode.addChildNode(cylindernode)
cylindernode.runAction(rotation)
return sceneView
}
func updateUIView(_ uiView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
}
typealias UIViewType = SCNView
}
I am having a error saying “ Use of unresolved identifier ‘rotationangle’ “ during using ‘rotationangle‘ in SCNAction.
You need to declare a #State in your ContentView and set the value of the property in your Button action.
You then need to declare an angle property in your SceneKitView as a #Binding and use that value for it to work properly.
I haven't yet tested this out. Keep in mind that you don't have cone declared anywhere so I'm not sure what that is.
import SwiftUI
import SceneKit
struct ContentView: View {
#State var rotationAngle: Float = 0.0
var body: some View {
VStack {
Button(action: {
// What to perform
self.rotationAngle = 180.0
}) {
// How the button looks like
Text("90°")
.frame(width: 100, height: 100)
.position(x: 225, y: 500)
}
SceneKitView(angle: self.$rotationAngle)
.frame(width: 300, height: 300)
.position(x: 0, y: 200)
}
}
}
//SceneKit
struct SceneKitView: UIViewRepresentable {
#Binding var angle: Float
func degreesToRadians(_ degrees: Float) -> CGFloat {
print(degrees)
return CGFloat(degrees * .pi / 180)
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
// Scene properties
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
return sceneView
}
func updateUIView (_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
if self.angle > 0 {
// Cylinder properties
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
// cone.firstMaterial?.diffuse.contents = UIColor.green
// Pivot and rotation of the cylinder
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
sceneView.scene?.rootNode.addChildNode(cylindernode)
//Line with an error: Use of unresolved identifier ‘rotationangle’
let rotation = SCNAction.rotate(by: self.degreesToRadians(self.angle), around: SCNVector3 (1, 0, 0), duration: 5)
cylindernode.runAction(rotation)
}
}
}

SDL_RenderPresent() not working

I am trying to draw a grid in a window using sdl2. The program when it is run, sometimes, the SDL_RenderPresent() even though being called, is not working resulting in a blank transparent window. Other times, the program is giving the correct output.
#include "SDL.h"
#include <stdio.h>
#include <stdlib.h>
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
const SDL_Color BLACK = { .r = 0, .g = 0, .b = 0, .a = 255 };
const SDL_Color WHITE = { .r = 255, .g = 255, .b = 255, .a = 255 };
const SDL_Color GRAY = { .r = 128, .g = 128, .b = 128, .a = 255 };
const SDL_Color RED = { .r = 255, .g = 0, .b = 0, .a = 255 };
const SDL_Color MAROON = { .r = 128, .g = 0, .b = 0, .a = 255 };
const SDL_Color BLUE = { .r = 0, .g = 0, .b = 255, .a = 255 };
const SDL_Color NAVY = { .r = 0, .g = 0, .b = 128, .a = 255 };
const SDL_Color LIME = { .r = 0, .g = 255, .b = 0, .a = 255 };
const SDL_Color GREEN = { .r = 0, .g = 128, .b = 0, .a = 255 };
const int P_SIZE = 10;
void draw_grid(SDL_Renderer *renderer)
{
SDL_Color prev;
SDL_GetRenderDrawColor(renderer, &prev.a, &prev.g, &prev.b, &prev.a);
SDL_SetRenderDrawColor(renderer, GRAY.r, GRAY.g, GRAY.b, GRAY.a);
// drawing the axes
SDL_RenderDrawLine(renderer, SCREEN_WIDTH / 2, 0,
SCREEN_WIDTH / 2, SCREEN_HEIGHT); // the Y-axis
SDL_RenderDrawLine(renderer, 0, SCREEN_HEIGHT / 2,
SCREEN_WIDTH, SCREEN_HEIGHT / 2); // the X-axis
SDL_SetRenderDrawColor(renderer, BLACK.r, BLACK.g, BLACK.b, BLACK.a);
// drawing the grid
for (int i = 0; i <= SCREEN_WIDTH; i += P_SIZE) {
if (i == SCREEN_WIDTH / 2) {
continue;
}
SDL_RenderDrawLine(renderer, i, 0, i, SCREEN_HEIGHT);
}
for (int i = 0; i <= SCREEN_HEIGHT; i += P_SIZE) {
if (i == SCREEN_HEIGHT / 2) {
continue;
}
SDL_RenderDrawLine(renderer, 0, i, SCREEN_WIDTH, i);
}
SDL_SetRenderDrawColor(renderer, prev.a, prev.g, prev.b, prev.a);
}
int main()
{
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "\nUnable to initialize SDL: %s\n", SDL_GetError());
return 1;
}
atexit(SDL_Quit);
SDL_Window *window =
SDL_CreateWindow("Clipping",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH, SCREEN_HEIGHT,
SDL_WINDOW_SHOWN
);
SDL_Renderer *renderer = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (renderer == NULL) {
fprintf(stderr, "\nRenderer could not be created:%s\n", SDL_GetError());
return 1;
}
SDL_SetRenderDrawColor(renderer, WHITE.r, WHITE.g, WHITE.b, WHITE.a);
SDL_RenderClear(renderer);
draw_grid(renderer);
SDL_RenderPresent(renderer);
int quit = 0;
while (!quit) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
quit = 1;
break;
default:
break;
}
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
return 0;
}

Resources